People sometimes think dead objects are toxic. They aren’t. They’re segments of memory that can be recycled. There is no difference between unreachable “uncollected” objects and “unallocated” space. Garbage collection is a bad metaphor.
Memory is either tagged free or used. Unused (dead) regions are referred to as garbage because there are no object pointers to it that define what the data means; not because it stinks.
Garbage collection is not the process of picking up or moving dead objects. In fact some GC processes are implemented by moving live objects. It makes no sense to “move” dead regions because the slots are “empty” whether they store zeros or ones. Moving meaningless ones and zeros to a new slot just doubles the number of slots filled with meaningless ones and zeros. (Well actually it would only double if the destination did not also store garbage, which would be a bad thing because you never want to interfere with live objects.)
Garbage collection mainly operates in two main ways: 1) Finding live objects and putting a “do not throw away” sticker on it, 2) Finding live objects and moving them to a safe space so they don’t get mashed into bits and melted down to make something new. Garbage collection is a misnomer. The point at which garbage is reused (or “collected”) is actually when new is called. (Note that the GC process makes this more efficient than the C++ allocation process. It comes down to either flipping a bit, removing a linked list node, or incrementing a counter.)
Imagine a post office or storage facility. If you have 1000 boxes and 500 are empty, it doesn’t matter that ten belong to people who haven’t retrieved their stuff in 20 years. If 1000 boxes are full and 200 are unused and you get new customers, you might want to empty the 200 abandoned boxes to make room for them. You would not send someone to patrol with a stop watch to empty single boxes as soon as possible. GC does not work that way either. If you have enough room to serve all your customers there is no reason to expand. Nor would you physically destroy storage space because you may want to use them later and it would be a waste of money both to demolish and rebuild them. (This is kind of how C++ works.) “Garbage collection” (actually scanning, marking, or copying) never needs to be performed if new is never called, so GC is usually delayed as long as possible or until it is scheduled based on what the program (the JRE) thinks is optimal. The JRE will no better than you because it is designed for the platform it runs on and can talk to the OS.
Nulling unreachable references is unnecessary because GC is a process of finding unreachable objects. If something will become unreachable it does not matter what it references because a reference from an unreachable object does not make an otherwise unreachable object reachable.
When the process terminates, all objects never be used again. Therefore there is no need to use GC because GC is only used to find free space.
If you did not know this is how garbage collection works, then you were not equipped to try to “optimize” memory management. The process is more detailed, but these are just the fundamentals. Urban legends about GC, bad metaphors, and outdated thought based on how fairly ignorant C programmers think memory management works are very far removed from reality.