Why doesn't the Java garbage collector collect

2019-09-06 18:32发布

问题:

I'm playing with a horrifying data structure which basically is a tree, and each node stores references to its children in a HashMap object. I'm having troubles freeing memory whenever I need to get rid of the root and all its subtrees except one, by setting this latter subtree as the new root. I thought it might be some bug in my data structure, maybe some reference that I forgot to be there, so nothing becomes eligible for garbage collection. But I wanted to try something much simpler first, and implemented the following test:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

public class MyNode {
        MyNode next;
        int somedata;

        public MyNode(MyNode n) {
            next = n;
            somedata = 0;
        }

        public static void main(String[] args) throws IOException {
            MyNode p = new MyNode(null);
            BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
            for (int i=0; i<10000000; i++) {
                MyNode n = new MyNode(p);
                p = n;
            }
            while (p!=null) {
                MyNode p1 = p.next;
                p.next = null;
                p = p1;
            }
            in.readLine();
        }
}

When main reaches in.readline(), I can see in htop that the process still has 250MB or so allocated for itself, and nothing gets freed. I obviously first tried to simply do

p = null;

instead of the while loop. But it didn't work so I came up with the previous code.

回答1:

Your question is somewhat unclear - are you actually getting OutOfMemoryErrors? What are you trying to solve? There are a few reasons you are seeing the behavior manifested in your test case:

  1. Garbage isn't collected the moment something is no longer reachable from a GC root - it simply becomes eligible for garbage collection. GC typically is only triggered when there is an allocation failure. Since you aren't actually allocating anymore memory in your loop that nulls out the references, it is entirely possible the GC just hasn't run yet.

  2. Even when garbage is collected, the memory in the heap is typically not returned to the OS - so looking at it from the perspective of the OS will not yield an accurate answer. Using VisualVM or tools like jmap and jhat would be the best way to figure out what is actually still on the heap.



回答2:

The behavior of the garbage collector is complex, and different garbage collectors can use fundamentally different methods. You can't expect garbage to be instantly reclaimed, even if you explicitly invoke it.



回答3:

I have seen this before.

htop is not the best way to measure memory utilization by JVM. It keeps showing high mark. VM also likes to keep its heap allocated as high as seen.

Might I suggest using visualgc or jconsole to track or jps and jpstat that ship with JVM. http://java.sun.com/performance/jvmstat/#Tools

Also, you have a loop that creates lots of objects quickly, so gc might now have time to kick in. System.gc is not guaranteed to actually get executed. It is a hint, but it usually works.

Take a look also at this post about Java heap utilization: http://it.toolbox.com/blogs/lim/how-to-get-details-on-jvms-heap-utilization-10609



回答4:

You can manually call garbage collection once in 10000 loops using the code

System.gc();

However there are some side-effects such as extra cpu time used by garbage collector.