3

我知道很多人之前都问过这个问题,但我关注的不是性能,而是操作的内存占用。

考虑以下虚拟类:

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();在每个循环结束时调用吗?

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

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

4

6 回答 6

4

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

在您的情况下,这没有什么区别:一旦您写入wbuffer = new byte[n];前一个字节数组,就会变得无法访问,因此有资格进行 GC。

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

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

于 2013-08-04T07:33:33.357 回答
1

您误解了链接到的问题。

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

但是之间

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

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

存在内存和性能差异。

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

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

byte[] wbuffer;
for (...) {
   wbuffer = new byte[size];
}
于 2013-08-04T07:36:10.163 回答
0

无论您byte[] wbuffer; 是在循环内部还是外部声明,它都不会因为它只是一个参考变量而产生太大影响。

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

如果您可以在每次循环迭代中以某种方式使用相同的数组,那么您肯定会节省内存,否则即使您在这里和那里洗牌,性能也不会存在真正的差异。

于 2013-08-04T07:33:57.573 回答
0

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

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

于 2013-08-04T07:37:08.890 回答
0

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

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

于 2013-08-04T07:37:25.703 回答
0

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

于 2013-08-04T07:38:53.243 回答