【问题标题】:Java Garbage Collection on Stack-Based Arrays基于堆栈的数组上的 Java 垃圾收集
【发布时间】:2013-02-20 10:11:50
【问题描述】:

假设我有以下代码:

public void process() {
    byte[] data = new byte[size]; 
    ... // code that uses the above data
    longProcess(); // a very long running process that does not use the data.
}

假设数据没有在程序的其他任何地方引用,JVM 是否足够聪明,可以在长进程仍在运行时允许数据被垃圾收集?

如果没有,会添加

data = null;

在漫长的过程允许这种情况发生之前?

【问题讨论】:

  • 你可以把代码用括号括起来。
  • data = null 使其有资格被垃圾收集。
  • 如果设置为null,你就无法到达它了,GC!
  • 不同的 JVM 可能表现不同...
  • @Affe 数组不驻留在 Java 的堆栈中,只是对它的引用。

标签: java garbage-collection


【解决方案1】:

这取决于 JVM。我尝试过的 Oracle JVM 版本(1.6.0_41 和 1.7.0_09)默认情况下不执行此优化。但是,1.7.0_09 确实会在开启积极优化时执行它。

这是我进行的测试:

public class Main {
    public static int g() {
        int n = 100000;
        int arr[][] = new int[n][];
        for (int i = 0; i < n; ++i) {
            try {
                arr[i] = new int[100000];
            } catch (OutOfMemoryError ex) {
                return i;
            }
        }
        return -1;
    }
    public static void f1() {
        int arr[] = new int[1000000];
        System.out.println(g());
    }
    public static void f2() {
        int arr[] = new int[1000000];
        arr = null;
        System.out.println(g());
    }
    public static void main(String[] argv) {
        for (int j = 0; j < 2; ++j) {
            for (int i = 0; i < 10; ++i) {
                f1();
            }
            System.out.println("-----");
            for (int i = 0; i < 10; ++i) {
                f2();
            }
            System.out.println("-----");
        }
    }
}

使用具有默认设置的 JVM 1.7,f1() 在 3195 次迭代后始终耗尽内存,而 f2() 始终管理 3205 次迭代。

如果代码使用带有-XX:+AggressiveOpts -XX:CompileThreshold=1 的Java 1.7.0_09 运行,图片会发生变化:两个版本都可以进行3205 次迭代,表明HotSpot 在这种情况下确实执行了这种优化。 Java 1.6.0_41 似乎没有这样做。

在我的测试中,限制数组的范围与设置引用 null 的效果相同,如果您认为应该帮助 JVM 尽快收集数组,这可能是首选。

【讨论】:

  • 您是否尝试过使用激进的 opts 标志?
  • -XX:+AggressiveOpts -XX:CompileThreshold=1 主要是为了尝试强制它编译优化
  • @radai:很好的建议。完全改变图片(见编辑)。
【解决方案2】:

鉴于编写的代码,数组肯定不会在 longprocess() 执行期间被垃圾收集,因为堆栈上仍然存在对数组的范围引用。一旦声明了该数组,在删除所有对它的引用之前,它将无法进行垃圾回收。你的线路

data = null;

将删除对它的一个引用,尽管根据您的处理代码,它可能不是唯一的引用。如果所有引用都已删除,那么垃圾收集器可能会在 longprocess() 返回时收集该数组的内存,尽管这不能保证。

【讨论】:

    【解决方案3】:

    如果没有对数据的引用,那么 GC 将完成这项工作。

    【讨论】:

    • 这没有回答问题。编辑:顺便说一句,我不是反对者:)
    • 他的意思是对数组的引用存在于范围内,但它不用于程序的其余(长时间运行)执行。我认为这个问题可以更好地表述:“如果引用存在但未使用,数据可以被垃圾收集吗?”
    【解决方案4】:

    只有在处理方法完成后,数据数组才会被释放内存。因此,如果您希望编译器释放内存,则必须在代码中显式添加 data = null。

    垃圾收集器仅释放没有可用有效引用的内存,并且没有其他方法可以再次指向该内存。

    【讨论】:

      猜你喜欢
      • 2013-12-30
      • 2014-05-13
      • 2014-03-27
      • 2012-06-03
      • 2023-03-17
      • 2010-12-13
      • 2011-02-25
      • 1970-01-01
      相关资源
      最近更新 更多