2017-04-20 28 views
0

我目前正在优化我的应用程序,并且我注意到它占用了近1G内存。我做了一些分析,发现问题: 我通过在我制作的纹理类中创建保存像素数据的int数组来分配大量内存。 但是这让我感到困惑,因为当创建一个新的数组时,旧的数组不再被使用,并且没有任何地方引用它。Java - 未引用的阵列仍然占用内存

我已经写了重新产生此问题的测试程序:

public class Main { 

    static class ArrayHolder { 
     int[] array; 

     void init() { 
      array = null; 
      array = new int[4000]; 
     } 
    } 

    public static void main(String[] args) { 
     ArrayHolder holder = new ArrayHolder(); 
     for (int i = 0; i < 1000000; i++) { 
      holder.init(); 
     } 
     System.out.println(holder.array.length); 
     try { 
      Thread.sleep(5000); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

当我运行这段代码的程序使用最多的RAM 600兆字节。我不明白为什么,因为在ArrayHolder中调用init()方法时,数组字段被设置为新数组,并且旧数组的引用丢失。这并不意味着它不再占用空间了吗? 对不起,如果这是一个没有问题的问题,但有人可以解释这是为什么,我在这里做错了什么?

+0

您的旧阵列只能在gc的判断下启动。如果gc在重新分配内存后很快运行,那么该空间将被回收,否则它不会。 – SMA

+0

大型阵列被添加到终身空间,并且只有填满时才会默认进行GC编辑。例如睡眠不会触发GC。简而言之,像这样创建大量的大型数组很不理想,原因很多。 –

回答

1

array参考到Java中的数组。在这方面,它们像来自java.lang.Object的任何其他常规对象。一旦不再有对一个数组的引用,它就会被安排进行垃圾收集,这不一定会立即发生。

释放内存中的这种延迟不太可能导致您的麻烦:垃圾收集器将在需要时收集未提交的对象。

请注意,即使垃圾收集器确实运行了,但仍无法保证进程将内存交还给操作系统,否则操作系统将从进程中夺回内存。

+0

谢谢你指出。你如何着手减少这个特定例子的内存使用量?或者你会把它留给GC?因为对于简单的应用程序来说1演出似乎有点太过分了。 – RagingRabbit

+0

我只会信任GC。 1GB在我的192GB机器上仅仅是微不足道的,因此如果CPU繁忙,1GB的尖峰不足以触发我盒子上的GC。 – Bathsheba

+0

我使用JVisualVM再次运行程序,并使用它执行垃圾回收,但内存使用情况未发生变化。 GC是否决定不收集或是一个问题? – RagingRabbit

2

仅仅因为你的对象没有引用并不意味着它不会占用任何内存。 GC必须运行,然后才能从内存中“移除”。你可以在内存中没有引用的对象占用空间。检查GC是否已经运行,如果没有明确调用它(不是一个好主意)。此外,我建议您查找是否确实有一些对象的强引用。

您使用System.gc()明确调用GC。

您可以添加诸如'-XX:+ PrintGCDetails'之类的JVM参数来检查GC是否已经运行。

+0

他将如何检查垃圾收集器是否运行,以及如何调用它,以及为什么他不应该这样做? – bleistift2

+0

@ bleistift2 - 编辑我的答案,添加你所需要的。至于为什么调用GC明确不是一个好主意,去低谷http://stackoverflow.com/questions/66540/when-does-system-gc-do-anything – TheLostMind

+0

请注意'System.gc()'是咨询。 – Bathsheba