我正在尝试使用Java及其允许内存泄漏的能力。我写了这个简单的代码:
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) {
class Obj {
int i;
Obj(int i) {
this.i = i;
}
}
List<Obj> list;
while(true) {
list = new ArrayList<Obj>();
for(int i = 0; i < 1000; i++) {
Obj o = new Obj(i);
list.add(o);
}
try {
Thread.sleep(1); //<-- added to give the gc time to trash the previous iteration
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
执行此操作时(特别是每次迭代时将更多对象添加到列表中),您可以快速查看使用的内存量。我能够非常快速地达到2千兆字节,每次迭代都会添加10000个对象。看起来好像列表(以及利用“链接列表”列表的其他类型的对象,如函数)不喜欢被删除。
当我没有将对象添加到列表中时,内存堆根本没有增加(意味着垃圾处理正在完成它的工作)。我尝试将列表重置为每次迭代时为null,然后重新声明它以及调用clear()方法。似乎没什么用。每当我在循环中使用List时,我的RAM就会求助。
那么,为什么会这样呢?为什么垃圾处理不会在每次迭代时都摆脱列表而不是让它们堆叠起来? List接口是否不允许这种用法?我只是不给垃圾处理足够的时间来摆脱最后一个实例?
答案 0 :(得分:5)
你的代码很好。除非你在JVM中遇到了一些晦涩难懂的漏洞,否则也没有内存泄漏。可能发生的事情是你为JVM提供了大量的内存,并且决定利用那个内存。
通过实验您的代码,我能够重现您所看到的内容的唯一方法是配置一个非常大的年轻代堆(-Xmn
)。 JVM不需要运行垃圾收集器,直到年轻代已满,因此该过程最终会占用大量内存。
然而,当年轻代堆变满时,GC会运行并收集所有无法访问的对象。
在下面的屏幕截图中,右上角的图显示了堆大小:
正如你所看到的,在19:47:50之前有一个大幅下降。这是年轻一代充满的时候,当GC运行并收集了所有旧列表时。
请注意,如果您使用操作系统工具来监控内存使用情况,则在运行垃圾收集器时可能看不到丢弃。当释放一些堆对象时,它们曾经占用的内存通常不会被释放回操作系统。但是,它可以通过相同的过程重复使用。
如果内存使用有问题,您需要重新访问您提供给JVM的选项。
答案 1 :(得分:0)
在list
循环中的每次迭代后,您需要将null
设置为while
。通过这种方式,GarbageCollector可以安全地清理它。
这也可以解决这个问题。 http://docs.oracle.com/cd/E19159-01/819-3681/abebi/index.html
答案 2 :(得分:0)
(通常情况下,我只是评论并询问您设置的VM选项,但我没有足够的重复点来做到这一点)
我只是猜测,但也许你将-Xms设置为1GB? 此代码不会泄漏,但根据您的VM选项,该过程可能仍会占用大量内存。
答案 3 :(得分:0)
代码中没有内存泄漏。你可以100%肯定。 但是,这并不意味着即使所有当前活动对象都小于堆,也不会耗尽内存。
您无法使用像您这样的代码测试垃圾回收。每次都不会调用GC,也不会立即清除所有内容。如果您有大量数据以快速刷新,那么您的GC将没有时间启动,并且您的代码可能会内存不足。
了解内存泄漏是通过分析工具进行测试而不是通过监视应用程序是否内存不足来进行测试也很重要。