I have to maintain a j2me application. There is a lot of System.gc() inside the application. Before calling System.gc() all the attributes are setted to null. Does setting attributes to null make sense? Does calling System.gc() make sense? Shouldn't the jvm call the garbage collector when is necessary?
Setting attributes to null (thus destroyign some references to objects) would GC to claim those objects. Maybe they were no longer needed, and devs wanted to free some memory?
I've heard that not every J2ME target has a GC. But if it does, it makes sense to call System.gc() so garbage collector wouldn't kick in when you're doing some realtime stuff.
On a good modern VM it generally makes little sense call System.gc(). It generally never makes sense to set attributes to null unless that is the only way you're making them unreachable.
On less sophisticated VMs, that you might get on mobile devices, it could make sense to null a reference if it is no longer used part way through a method but is still in scope. (On modern desktop VMs like Hotspot, this is generally no longer necessary-- the VM has your code in front of it and can generally work out whether or not you're still going to use a local reference, irrespective of scope.)
Now, coming back to System.gc(), it is true that mobile VMs have historically been simpler than the fairly sophisticated "fully fledged" VMs that we're used to such as Hotspot. However:
- mobile VMs have come on a lot in recent years-- object throughput, like other aspects of performance, has definitely improved on many devices;
- in any case, System.gc() has no consistent, well-defined behaviour. On some devices, it may do precisely nothing; on others, as with desktop VMs, it may cause a type of garbage collection that actually hurts performance compared to the normal, "gradual" garbage collection that the VM automatically performs while the app is running.
So I would be very wary of using System.gc().
It's a big urban legend in the Java world that calling System.gc() will actually, reliably do something for you. It won't.
It is a mere suggestion that the JVM attempt to do something. The Javadoc for the method is purposefully ambiguous in this regard.
The other big bugbear is nulling references. When a reference goes out of scope, it is already nulled by the VM, doing that yourself is redundant and misleading. If there are other references still around, they will still be available and your assignment will do nothing (unless you go and visit every other reference available -- but then, what's the point? that's what the VM is already doing).
So to answer your questions:
- Setting attributes to null rarely makes sense. Design your software properly, so that references go out of scope when you're no longer using them.
- Calling System.gc() rarely makes sense. Let the JVM do the work for you -- that's why we use a VM instead of managing our own memory.
- Yes, let the VM manage your memory, it will GC when it needs to. However, if you've got poorly designed software, it can defeat the GC in the VM. In this case, track down your memory leaks and refactor the software (easier said than done, though).
The reason for all the nulling of attributes and System.gc() calls is definitely part of a fight against device fragmentation. It's easy enough to say that you should never null a value yourself, that you should let it just go out of scope, and actually this is the appropriate way for PC development. However, the mobile space is a completely different animal and it would be naive to say that you can handle memory management the same way on a limited mobile device as you do on a PC that has gigs and gigs of RAM at its disposal.
Even now, the high-end smart phones have around 32-64MB of heap available to a J2ME application. Many devices are still around 1-4MB and often to meet carrier specification you will need to target even smaller heap sizes. The Razr only has 800K of heap and is still required to be supported by most carriers, due to the fact that so many of their subscribers still use the device.
Memory fragmentation happens because even though a block of memory is no longer in use, that it has been nullified, it is not yet available until it is garbage collected. Most mobile VMs only GC when memory is needed. So when it finally does run the GC, the heap could possibly be fragmented into tiny blocks of memory. When a larger block of memory needs to be allocated it will be unable to allocate to the smaller block. If you have enough of these smaller blocks polluting the heap, not supplying a block big enough for a larger object, eventually you will get an OutOfMemory exception, even though it may appear that you have tons of memory available.
For instance, imagine you create a local Object variable that requires 50 bytes. Next you allocate a member variable that will persist throughout the life of the application that requires 200 bytes of heap to exist. If you do this process over and over again, you could possibly get in the situation where you have thousands of blocks of memory that are only 50 bytes in length. Because you need 200 bytes for the persistent object, you will eventually be screwed and get an OutOfMemory exception.
Now if you created the same String object, used it, then nulled it yourself and called System.gc() (also you might want to call Thread.yield() to give the GC a chance to run) you would then have those 50 bytes available to create the new persistent object, avoid fragmentation and avoid the eventual OutOfMemory exceptions.
This is a very basic example, but if you have a lot of large images like in a game application, you run into this situation very quickly in the mobile space.
One last note, with BlackBerry devices you should avoid calling the garbage collector yourself as it is much more sophisticated (it will defrag the heap for you). Being more sophisticated makes it much slower (we're talking several seconds per run) than the normal down and dirty GCs on most devices.
All the answers to this question so far are entirely correct, mainly that you cannot rely on a System.gc() to do anything. It is platform specific and MIDP2 doesn't give any real specifications on how it should behave.
However it is normally implemented in a suitable way, although you cannot say "this object is now removed" etc, I believe it is acceptible and advisable to use the call under certain circumstances. For instance if your application needs to perform a large deletion then allocation (for instance resetting or changing states), making the call immediately after will tell the VM - now is a good time to collect.
James