Controlling Java's garbage collector

What I really dislike about C is that you have to manually manage memory yourself.

In Java, the beauty is that I can make my code nice and uncomplex without worrying about all the little things.

Currently I’m finding myself working around the GC in order to ensure there are no pauses for a lower end system.

I wish I could disable the GC until I call ‘System.gc()’.

At least I want to be able to ensure that the GC is only applied to generation 1 objects until I say it’s okay for it to collect object of other generations.

Is there anyway of controlling Java’s GC?
I have not read any articles about this, I checked Javalobby and Googled this up.

All I managed to find is information which I already have alternatives to; walking around the garbage collector, modifying code to a degree that I have a large mess which is a headache to keep up with.

Is there any possibility that Sun could implement strict GC controlling methods in system which allows the developer say “when” to the GC?
Increasing the memory of managed pool can only go so far.

There can be exceptions thrown if the managed pool is filled.
I just want tighter control of memory to a certain extent.

Erm… this is not offtopic. It should be moved to Performance Tuning, or Gameplay/Design.

[quote]What I really dislike about C is that you have to manually manage memory yourself.
[/quote]
Agreed. Manual memory management is a pain in the rear that can often be done just as efficiently (sometimes more) through automatic means.

Did you know that programmers tend to create performance problems by fighting their API’s and platforms? Don’t fight the garbage collector. You’re only going to make the problem worse. Instead, you need to work with the GC and make sure it runs as fast as possible. Use short lived objects whenever possible, but make sure the intialization cost of those objects is lightweight. This should cause the GC to run for very short periods of time, as it will merely need to axe the nursery instead of checking and defragmenting the long-term heap.

[quote]It should be moved to Performance Tuning, or Gameplay/Design.
[/quote]
This is really a discussion about Sun and the GC.
I’ve been told to keep generic Java topics out of other forums unless they are specific to a game.
This topic started about a game, but it is really a generic discussion.

This topic also covers other reasons why people would need more control over memory.

The problem is I have very, very few short lived objects.

If you had 256 heavyweight objects, it runs real slick on my A64 3K+, 1GB RAM.
It doesn’t run as slick on my brothers P3 800MHz, 512MB RAM.

Of course you are right, the reason could be because of my code. However I do not intentionally fight the GC, I work around it.

Currently I write my code so that no new objects are created unless there is a class which specifies it for a good reason.
The one thing I don’t like is that because of the way I’m now writing code it’s making my program look large and complex.

Much like an app in C.
The reason I continue to use Java is because it is much more friendlier which makes it quicker to write programs, and C apps can cause unexpected memory problems.

[quote]The problem is I have very, very few short lived objects.

If you had 256 heavyweight objects, it runs real slick on my A64 3K+, 1GB RAM.
It doesn’t run as slick on my brothers P3 800MHz, 512MB RAM.
[/quote]
Hmmm… well, my desktop is a PIII 733 w/512MB. I can’t say I’ve seen many problems with GC pauses. In fact, 800 MHz should be more than fast enough to wipe out any pauses that might be caused by the garbage collector. (Especially with as few as 256 objects.) Are you sure the issue isn’t caused by something else blocking? For example, could it be caused by the sound thread occasionally blocking? Or perhaps his video drivers are out of date?

If you have a test case somewhere, I might be able to get a better feel for the problem. :slight_smile:

When I get the time I will create a test case.
The objects are pretty big.

Just a single class contains several objects in itself.
Other classes contain enormous amount of variables 10+.

I guess I could optimise it by reducing the number of variables.

There is no sound in the program and the video drivers on my bro’s computer are up to date, he has a Geforce 3 (NV20).

Uhm… my machine is really old. 500mhz and 128mb ram. Duh. I didn’t run into any gc problems lately. Only every once in a while and each time it’s because I’m doing something horrible stupid like creating hundrets of byte buffers per second.

However, internal allocation/deallocation is very fast. I can create thousands of tiny little objects per second without any dropped frames. I could also write a 500byte file 100 times per second without any dropped frames (did that yesterday by accident).

However I do not intentionally fight the GC, I work around it.

Haha. Most likely your “working around” is to blame.

Well, install 1.5 on your bro’s pc. It seems to have the best gc so far.

And - of course - profile it. If you don’t do that, all you can do is guessing.

Both my bro and I use Java 1.5.

My “walking around” is merely adepting my program to not create objects during runtime.

I’ve been doing Java for slightly over 1.5 years.
You have more experience in dealing with these issues than I do.

Technically I’m still learning Java. Not syntax or libraries, just dealing with the GC and the like.
I’ve read articles and taken tips from them about the GC, not too sure if it’s a good idea with Java 1.5.

There’s something funny up with GC on 1.5. It’s causing major juddering of the entire machine. Well, 1.5 is - I don’t know if it’s the GC or some other strangeness, because I don’t have the tools or time to investigate.

If your application is spending 1% of its CPU time on GC, then that’s also 1% of a frame time doing GC. So don’t worry about it! If you’re spending significantly more you’ve got a problem and should look at tuning it by tweaking GC runtime parameters and using different collectors, and then finally by changing your code to using a few less allocations.

Cas :slight_smile:

[quote]If your application is spending 1% of its CPU time on GC, then that’s also 1% of a frame time doing GC.
[/quote]
Uh, nonsense. But I get your point, it’s usually not worth worrying about.

However, worst case (you are using 100%CPU), 1% could mean that it takes a full frame time once out of every 100 frames. That’s going to look ugly.

Of course typically you are not using 100% CPU… but then you can scale the above accordingly… at 50% CPU you might see a glitch once every 200 frames, or not. It all depends on what your frame rate is, how much time per frame you need to set up the next frame, and how long the GC pause is.

When i need to dealocate a very complex structure like a graph or a tree i usualy don’t just “myTreeRef = null”, instead i usually make myTreeRef.dispose() which will in turn set every child ref to nulls. Don’t know if this helps the gc or not. Another solution is to have a Store class that will collect unsused mutable objects that can be reused later.

Yes, I was simplifying the problem a bit too much. But by and large the GC doesn’t seem to exhibit this behaviour. That new throughput goal stuff in 1.5 is handy too for ensuring that it meets your frame time requirements (trouble is it’s slow to adapt and always adapts after it fails to meet its targets…)

zingbat - no, this doesn’t help one bit.

Cas :slight_smile:

AFAIK the GC shouldn’t be running until your RAM usage behaves in a funny manner or the heap is filled.

Here is a test case, the libraries or the engine core are nowhere near completed. I’ve spent an hour or 2 doing this.
Please don’t laugh, I have other things to do as well.

If the guy with P3 500MHz CPU doesn’t have any issues running this then I accept that my brothers PC is screwed up.

http://members.optusnet.com.au/ksaho/dlds/test.jar

etc.

You’re creating 0.25mb of garbage every frame I think! On this here 2GHz machine it’s taking 0.3ms to collect it, every 1-2 frames. This is acceptable but not optimal - on oNyx’s cranky old grid it’ll probably be more like 1.2ms.

Now I wouldn’t ordinarily worry about it except for the fact there’s absolutely feck all going on in your game and you’re already using 5% of the framerate on a 500MHz CPU.

So I loaded it into eclipse and poked around your code, and I see you’re using that nasty new 1.5 iterator construct, and what’s more you’re nesting them in an n * n for collision detection, and what’s more you’re doing twice as many collision detections as you have to :wink:

Change to a simple ArrayList walk through with for(int i = 0; i < characters.size(); ) loops:


            //collision
            int n = characters.size();
            for (int i = 0; i < n; i ++) {
                  GameCharacter src = (GameCharacter) characters.get(i);
                  for (int j = i + 1; j < n; j ++) {
                        GameCharacter dest = (GameCharacter) characters.get(j);
                        if (src.hasCollided(dest)) {
                              src.respondToCollision(dest);
                              dest.respondToCollision(src);
                        }
                  }
                  if (pc.hasCollided(src)) {
                        pc.respondToCollision(src);
                  }
            }

And the vast majority of the rest of your garbage is this line:


            return this.location.getLocation();

… which when tuned out, means your game produces no garbage at all.

Cas :slight_smile:

So, are you saying that iterator is a complete and utter waste of time for real programming, or are you just disapproving of it and the other problems are 99% of the problem?

I got rid of “this.location.getLocation()” to “this.location”.
Will this get rid of garbage?

I don’t understand why garbage is generated when you access a reference of an object?

In the Java 5 enhanced for loop it is effectively getting the reference of the ArrayList’s iterator and using it.
Why would it create garbage?

On the topic of collisions, I didn’t know it was executing twice. I’m going to have to read over your code because it looks similar.

Thank you.

PS: Where can I get the tools you have in Eclipse?

BTW: what did you thin of my scripting system? :smiley:

Point.getLocation() creates a brand new copy of the point. Duh! You call this a lot.

Using Iterators is fine in a simple outer loop but in this particular case KILER was using an Iterator inside an Iterator. This creates an exponential amount of garbage when the number of elements is large - for a simple 200 odd entities he’d have created 40,000 iterators in a single frame (doing it wrongly as he was) or about 20,000 iterators, doing it correctly, as the algorithm I have posted does, and if the object of the exercise is to reduce garbage, well - there you go :slight_smile: In just one second of game time he’d have created and collected 38MB of iterators assuming they’re about 32 bytes apiece.

Your collision detection system does not execute twice, it just performs twice as many collision detections as it needs to. If A collides with B there is no need to test if B collides with A.

KILER may not be aware that ArrayList.iterator() creates a new Iterator instance every time it’s called.

I have no special tools in Eclipse, I just looked at the code and removed all the iterators and fixed the getLocation() call.

Cas :slight_smile:

Oh I see where the problem is.

"Point.getLocation() creates a brand new copy of the point. Duh! You call this a lot. "

Whoops, static access.
Point made. Didn’t see that.

So iterators are bad when used in parallel?
Does a single iterator still creates tiny amounts of garbage?

I’ve read that using iterators boosts your performance.
Maybe I was reading the wrong thing.

So arrayList.iterator() creates an iterator on demand?
I thought the iterator was creating when you initialise the list.

38MB of garbage is a lot. I’m definitely learning. Heheh, thanks. :slight_smile:

Just noticed myself while looking from the code.
I could have just moved my player into the main loop. After all, there’s no need to cycle through the same objects twice.
DOH!!!

Then where did you get the garbage count? :wink:

NOTE: You casted where you didn’t need to, in the collision loop. :wink:

Also a more optimised loop:


//collision
            for(int y=0; y < characters.size(); y++)
            {
                  GameCharacter hndY = characters.get(y) ;
                  
                  for(int x=0; x < characters.size(); x++)
                  {
                        GameCharacter hndX = characters.get(x) ;
                        
                        if(y != x)
                        {
                              if(hndX.hasCollided(hndY))
                              {
                                    hndX.respondToCollision(hndY);
                                    hndY.respondToCollision(hndX);
                              }
                        }
                  }
                  
                  if( pc.hasCollided(hndY))
                  {
                        pc.respondToCollision(hndY);
                        hndY.respondToCollision(pc);
                  }
            }

Thanks, I really do enjoy learning from you guys. :slight_smile:

PS: This thread has now officially involved in my project.
I think I should have listened to the farsight given to me that this thread would ended up about my shoddy coding skills. :stuck_out_tongue:

Still doing 2x collisions… inner loop should start at outer loop + 1.

Cas :slight_smile:

[quote]Still doing 2x collisions… inner loop should start at outer loop + 1.

Cas :slight_smile:
[/quote]
Your way is better so I did it that way.
Shouldn’t “y != x” make sure collision isn’t done on the same entity?

Say there are A, B, C and D.

A vs B, C, D
B vs C, D (A+B were already checked)
C vs D (A+C and B+C were already checked)