我有这个类,我正在测试具有不同数据分布的插入。我在我的代码中这样做:
...
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树(),垃圾收集器就没有摆脱旧树。但为什么?我以为老树上的元素将无法再被访问,他们的记忆也会被清理干净。
答案 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())?
答案 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.WeakHashMap
和char[]
的池,并且我们在池中维护单独的“bins”大小(例如,我们总是分配大小为2的幂的数组)。我们的产品进行了大量的字符串构建,使用池显示了显着的性能改进。
注意:一般来说,GC做得很好。我们只是注意到,对于指向较大结构的小对象,GC似乎不能足够快地清理对象,尤其是当VM处于CPU负载之下时。此外,byte[]
只是帮助安排终结器线程做更多工作的提示。过于频繁地调用会导致性能受到重大影响。
答案 6 :(得分:0)
鉴于您只是为了测试目的而这样做,可能只是使用System.gc()
直接调用垃圾收集器(从而强制它进行传递)。如果存在内存泄漏,它将无法帮助您,但如果没有,它可能会为您提供足够的内存来完成测试。