关于java垃圾收集的问题

时间:2009-04-17 02:54:03

标签: java garbage-collection

我有这个类,我正在测试具有不同数据分布的插入。我在我的代码中这样做:

...

AVLTree tree = new AVLTree();

//insert the data from the first distribution

//get results

...

tree = new AVLTree();

//inser the data from the next distribution

//get results

...

我正在为3个发行版做这个。每个应平均测试14次,并从中删除2个最低/最高值以计算平均值。这应该做2000次,每次1000个元素。换句话说,它是1000,2000,3000,...,2000000。

问题是,我只能达到100000.当我尝试200000时,我用尽了堆空间。我在命令行中使用-Xmx将可用堆空间增加到1024m,它甚至没有用200000完成测试。我尝试了2048m,再次,它不起作用。

我在想的是,一旦我做了树=新的AVL树(),垃圾收集器就没有摆脱旧树。但为什么?我以为老树上的元素将无法再被访问,他们的记忆也会被清理干净。

7 个答案:

答案 0 :(得分:5)

垃圾收集器清理你的旧树对象应该没有问题,所以我只能假设你正在做的其他一些分配是而不是被清理。

答案 1 :(得分:5)

Java有一个很好的工具来监视正在进行的GC(或者不是你的情况),JKisualVM,它随JDK一起提供。

只需运行它,它将显示哪些对象占用了堆,您可以触发并查看GC的进度。然后,您可以将这些目标定位到池中,以便您可以重复使用它们,从而为GC节省工作。

另外看看这个选项,它可能会停止你得到的错误,停止程序,你的程序将完成,但它可能需要很长时间,因为你的应用程序将填满堆然后运行非常慢。

-XX:-UseGCOverheadLimit

答案 2 :(得分:1)

您正在使用哪个JVM以及您用于配置GC的JVM参数?

您的解释显示代码中存在内存泄漏。如果你有像jprofiler这样的工具,那么用它来找出内存泄漏的位置。

答案 3 :(得分:1)

没有理由不应该收集这些树,虽然我希望在内存不足之前你应该看到长时间暂停,因为系统运行了一个完整的GC。正如在这里所指出的那样,这不是你所看到的,你可以尝试使用-XX:-PrintGC,-XX:-PrintGCDetails,-XX:-PrintGCTimeStamps等标志运行,以便为您提供有关正在发生的事情的更多信息,可能还有某种大致的跑步数。您还可以明确告诉垃圾收集器使用不同的垃圾收集算法。

但是,我似乎不太可能。还有其他什么代码在运行? AVLTree类本身是否有可能保持其实例不被GC格式化?如何在该类上手动记录finalize()以确保(其中一些,至少)可以收集(例如,制作一些并手动调用System.gc())?

GC params here,一个关于来自太阳here的垃圾收集的很好的参考,非常值得一读。

答案 4 :(得分:0)

在每个对象的refcount变为零之后,不保证Java垃圾收集器进行垃圾收集。因此,如果您编写的代码只创建和删除 lot 对象,则可以在gc有机会运行之前占用所有堆空间。或者,Pax建议您的代码中存在内存泄漏也很有可能。

如果您只是在进行基准测试,那么您可能希望在测试之间使用java gc函数(在我认为的System类中),或者甚至为每个发行版重新运行程序。

答案 5 :(得分:0)

我们在服务器产品中注意到了这一点。当制造很多很快被丢弃的微小物体时,垃圾收集器无法跟上。当微小物体具有指向较大物体的指针(例如指向大char[]的物体)时,问题就更明显了。 GC似乎没有意识到,如果它释放出微小的物体,它就可以释放更大的物体。即使直接调用System.gc(),这仍然是一个巨大的问题(在1.5和1.6虚拟机中)!

我们最终做了什么以及我向您推荐的是维护一个对象池。当不再需要您的对象时,将其扔入池中。当您需要一个新对象时,如果池为空,则从池中获取一个对象或分配一个新对象。这也将比纯分配节省少量时间,因为Java不必清除(bzero)对象。

如果您担心池太大(从而浪费内存),您可以定期从池中删除任意数量的对象,或使用弱引用(例如,使用{{1 }})。使用池的一个优点是您可以跟踪分配频率和总计,并且可以相应地调整内容。

我们正在使用java.util.WeakHashMapchar[]的池,并且我们在池中维护单独的“bins”大小(例如,我们总是分配大小为2的幂的数组)。我们的产品进行了大量的字符串构建,使用池显示了显着的性能改进。

注意:一般来说,GC做得很好。我们只是注意到,对于指向较大结构的小对象,GC似乎不能足够快地清理对象,尤其是当VM处于CPU负载之下时。此外,byte[]只是帮助安排终结器线程做更多工作的提示。过于频繁地调用会导致性能受到重大影响。

答案 6 :(得分:0)

鉴于您只是为了测试目的而这样做,可能只是使用System.gc()直接调用垃圾收集器(从而强制它进行传递)。如果存在内存泄漏,它将无法帮助您,但如果没有,它可能会为您提供足够的内存来完成测试。