根据内存和GC在循环内部或外部声明对象



我知道以前有很多人问过这个问题,但我的重点不是性能,而是操作的内存占用。

考虑以下伪类:

public class MemoryDemo implements Runnable{
    private boolean run;
    public MemoryDemo(){
        run = true;
    }
    @Override
    public void run(){
        byte[] wbuffer; //Here
        final int n = ... //Some big quantity in order of millions.

        while(run){
            //byte[] wbuffer; or here?
            wbuffer = new byte[n]; //Reallocate every loop? or just keep the same memory?
            //Do stuff with the byte array here
            //Copies the data to the target buffer (Not shown here, from another class)
            System.arraycopy(wbuffer, 0, n, targetbuffer, 0, wbuffer.length);
        }
    }
}

我知道从这里到这里,人们都说在性能方面绝对没有区别,范围有限是更好的方法,但内存占用呢?

如果你从上面观察,你可以看到我正在分配一些相当大的数组,由于java没有删除函数/构造,我们不得不依靠垃圾收集器来释放内存。在我的应用程序中,我有几个这样的循环(它基本上是一个实时图像处理管道),我希望尽可能减少内存占用(即帮助GC以最好的方式完成它的工作)。

在垃圾回收方面,循环内声明和循环外声明(如果有的话)哪个更好?我知道GC只有在不再引用它的情况下才能释放内存,但我不清楚如果重新分配变量会发生什么(即,当循环重新启动,wbuffer对象再次被分配时)。由于内部循环变量失去了它的整个引用,它会首先被垃圾收集吗?还是当变量被重新分配时,它们都会被垃圾收集?我应该在每个循环结束时调用System.gc();吗?

此外,如果我从不重新分配变量(就像在循环中我从不调用新的byte[n]一样),假设我的代码可以写入字节数组中的所有字节,那么重新分配字节数组不是更好的方法吗(也是一种更丑陋的方法…)?

N.B不重新分配数组对我来说可能不是一个可行的选择(对于我的所有类),如果它确实是最好的选择,还请解释哪个是第二好的(内部/外部循环或没有区别)!

从内存角度来看,重要的是确定对象何时符合垃圾收集的条件。

在您的情况下,这没有什么区别:一旦写入wbuffer = new byte[n];,上一个字节数组就变得不可访问,因此符合GC的条件。

重用同一个数组将提高内存占用率,在这种情况下,您需要在循环之前声明它。

GC将在必要时运行。除了非常具体的用例之外,调用System.gc();通常是个坏主意——它实际上会对性能产生负面影响。

您误解了链接到的问题。

在这些问题中,问题是是否定义了变量,但分配的对象数量没有改变。因此,它并没有影响性能。

但在之间

byte[] wbuffer = new byte[size];
for (....) {
}

for (....) {
   byte[] wbuffer = new byte[size];
}

内存和性能存在差异。

在第二个中创建了更多的对象,这会影响性能和内存。

你搜索的问题解释了第二种形式和之间没有区别

byte[] wbuffer;
for (...) {
   wbuffer = new byte[size];
}

无论在循环内部还是外部声明byte[] wbuffer;,它都不会有太大区别,因为它只是一个引用变量。

主内存分配发生在这里wbuffer = new byte[n];,现在如果您在每个循环迭代中创建新的数组,那么GC肯定会很高。

若你们可以在每次循环迭代中使用相同的数组,那个么你们肯定会节省内存,否则即使在这里和那里混洗东西,性能也不会有真正的差异。

您不应该重新分配每个循环。你不应该每次都运行垃圾收集。

但事实上,这并没有什么大区别。

JVM的垃圾收集是完全不可预测的。不同的实现可能会执行不同的操作,仅Oracle的JVM就有多种垃圾收集器策略。

幸运的是,您使用的是一个基元数组。这大大减少了事情的复杂性。如果只调用new一次,则只会创建一个数组。编辑数组中的值,即使使用System.arrayCopy,实际上也会编辑这些值,而不是创建新数组。如果在循环之外声明数组,则垃圾回收甚至不会进入等式。

无论将声明变量放在循环内部还是外部都没有意义,因为每次迭代都会重新分配它。在这种情况下,您可能需要一个干净的零初始化字节数组,因此只有在不重用相同的字节数组的情况下,将其置于循环之外才有意义。

最新更新