System.gc()收集仍由局部变量引用的对象

时间:2018-09-21 06:01:36

标签: java garbage-collection jvm

当我运行以下程序时

public static void main(String[] args) {

    ArrayList<Object> lists = new ArrayList<>();
    for (int i = 0; i <200000 ; i++) {
        lists.add(new Object());
    }
    System.gc();
    try {
        Thread.sleep(Integer.MAX_VALUE);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

然后我丢了堆

jmap -dump:live,format=b,file=heap.bin 27648
jhat -J-Xmx2G heap.bin

ArrayList和200000对象丢失。

我不知道为什么JVM知道将不使用对象 以及为什么JVM判断此GC根目录不是引用。

1 个答案:

答案 0 :(得分:4)

局部变量本身不是GC的根。 The Java® Language Specification定义:

  

reachable 对象是可以在任何活动线程中进行的任何潜在连续计算中访问的任何对象。

很明显,它需要一个变量来保存对一个对象的引用,以便可以通过实时线程在“潜在的连续计算”中访问它,因此,此类变量的缺失可以用作易于使用的变量。 -检查是否有对象无法到达。

但这并不排除其他工作来确定仍由局部变量引用的无法访问的对象。规范甚至明确指出

  

可以设计程序的优化转换,以将可到达的对象数量减少到少于天真的被认为可到达的对象数量。例如,Java编译器或代码生成器可能会选择设置将不再用于为null的变量或参数,以使此类对象的存储可能会尽快收回。

在同一部分。

它是否实际取决于当前执行模式之类的条件,即该方法是解释运行还是已经编译。

从Java 9开始,您可以插入明确的障碍,例如

public static void main(String[] args) {
    ArrayList<Object> list = new ArrayList<>();
    for (int i = 0; i <200000 ; i++) {
        list.add(new Object());
    }
    System.gc();
    try {
        Thread.sleep(Integer.MAX_VALUE);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    Reference.reachabilityFence(list);
}

这将强制列表保持活动状态。

以前的Java版本的替代方法是同步:

public static void main(String[] args) {
    ArrayList<Object> list = new ArrayList<>();
    for (int i = 0; i <200000 ; i++) {
        list.add(new Object());
    }
    System.gc();
    synchronized(list) {
        try {
            Thread.sleep(Integer.MAX_VALUE);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

但是通常,您希望尽早收集未使用的对象。仅当您将finalize()与有关可达性的幼稚假设一起使用时,问题才可能出现。