在内存和GC方面声明循环内部或外部的对象

时间:2013-08-04 07:25:27

标签: java loops memory-management garbage-collection variable-declaration

我知道很多人之前已经问过这个问题,但我的重点不在于性能,而在于操作的内存占用。

考虑以下虚拟类:

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);
        }
    }
}

我知道来自herehere人们说它在性能方面完全没有区别,而且有限的范围是更好的方法,但内存占用情况如何呢?

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

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

另外,假如我的代码可以写入字节数组中的所有字节,那么如果我永远不会重新分配变量(因为我从不在循环中调用新的byte [n]),不会重新分配字节数组更好的方法(一个更丑陋的......)?

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

6 个答案:

答案 0 :(得分:4)

从内存角度来看,重要的是确定对象何时有资格进行垃圾回收。

在您的情况下,它没有区别:只要您编写wbuffer = new byte[n];,前一个字节数组就无法访问,因此符合GC条件。

重用相同的数组会增加内存占用,在这种情况下,您需要在循环之前声明它。

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

答案 1 :(得分:1)

您误解了与之相关的问题。

在这些问题中,问题是变量已定义,但分配的对象数量没有变化。所以,它并没有影响性能。

但在

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

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

存在内存和性能差异。

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

您搜索的问题说明第二种形式和

之间没有区别
byte[] wbuffer;
for (...) {
   wbuffer = new byte[size];
}

答案 2 :(得分:0)

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

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

如果不知何故你可以在每个循环迭代中使用相同的数组,那么你肯定会节省内存,否则即使你在这里和那里随机播放,性能也没有真正的差别。

答案 3 :(得分:0)

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

但实际上它并没有大不同。

答案 4 :(得分:0)

JVM的垃圾收集完全不可预测。不同的实现可能会有不同的执行,而Oracle的JVM本身就有多个垃圾收集器策略。

幸运的是,您正在使用原始数组。这使得事情变得相当复杂。如果只调用new一次,则只创建一个数组。即使通过System.arrayCopy编辑数组中的值,实际上也会编辑这些值而不是创建新数组。如果在循环外声明数组,垃圾收集甚至不会进入等式。

答案 5 :(得分:0)

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