JVM垃圾收集和分页内存架构

在讨论java和/或垃圾收集的最近10年中,我无法防范的唯一性能损失是垃圾收集算法在分页内存架构中运行时或多或少会中断,并且堆的部分内容正在变得越来越多分页。

Unix系统(尤其是Linux)积极地分析了一段时间没有被触及的内存,虽然这对于你的普通泄漏应用程序来说是好的,但它会在内存紧张的情况下杀死javas性能。

我知道最好的做法是保持最大堆小于物理内存。 (或者你会看到你的应用程序交换死亡)但是这个想法 – 至少在unix世界中,是内存可以更好地用于文件系统缓存等。

我的问题是:是否有任何分页(识别)垃圾收集算法?

你是对的,垃圾收集器和虚拟内存管理器必须协作,否则GC会废弃系统。 Matthew Hertz,Yi Feng和Emery D. Berger对此类GC /内核协作进行了调查。 为了获得良好的性能,他们不得不稍微扩展内核并调整垃圾收集器。

在高内存压力下,使用GenMS Java GC,他们的基准测试时间延长了160倍。 使用新的页面感知GC,基准测试仅慢了1.6倍。 换句话说,通过适当调整的GC,可获得100倍的性能提升。

http://lambda-the-ultimate.org/node/2391

我要争辩说,这不像你想象的那么大。

为了确保我们描述同样的事情:完整的集合需要JVM遍历对象图以识别每个可到达的对象; 留下的是垃圾。 在执行此操作时,它将触及应用程序堆中的每个页面,如果已将页面换出,则会导致每个页面出现故障。

我认为这是一个无关紧要的原因有以下几点:首先,因为现代JVM使用世代collections家,而且大多数物体永远不会走出年轻一代,几乎可以保证在常驻集中。

其次,因为移出年轻一代的物体仍然经常被访问,这再次意味着它们应该在居民集中。 这是一个更为脆弱的论点,事实上很多情况下,除了GC之外,长期存在的对象不会被触及(我不相信内存限制缓存的一个原因)。

第三个原因(可能还有更多)是因为JVM(至少是Sun JVM)使用了mark-sweep-compact收集器。 因此,在GC之后,堆中的活动对象占用的页数较少,再次增加了RSS。 顺便提一下,这是Swing应用程序在最小化时显式调用System.gc()的主要驱动因素:通过压缩堆,当它们再次最大化时,交换的次数更少。


另外,要认识到C / C ++对象的堆碎片可能会变得极端,年轻的对象会在较旧的对象中散布,因此RSS必须更大。

我不是专家,但任何一代的垃圾收集都应该有所帮助。 正在积极交换的页面可能包含较旧的对象而不是较新的对象,因此gc自然会较少地触及它们。

我还问了一个反问题:是否有任何unix分页算法可以识别垃圾收集? 如果一个给定的页面被定期(如果不是频繁的话)被拖入内存,那么也许它不是一个很好的候选人,毕竟被抛弃更多的磁盘缓存;-)

Unix系统(尤其是Linux)积极地分析了一段时间没有被触及的内存,虽然这对于你的普通泄漏应用程序来说是好的,但它会在内存紧张的情况下杀死javas性能。

请记住,这通常是可调整的设置 – 例如,Linux内核的vm.swappiness。 如果您想了解有关Linux上交换调优的更深入信息,您可以阅读我在此撰写的博客文章

是否有任何分页(识别)垃圾收集算法?

垃圾收集算法通常设计用于各种可能的程序作为输入和在大量可能的环境中操作; 他们的设计需要考虑到这一点。 我认为制作一个广泛有用的“分页感知”gc算法真的很有挑战性。 如果你在一个非常专业的环境中编写一个可以锁定事物的环境,那么我认为你很有可能产生一个好的结果。

在这个时代,寻呼程序是一个非常糟糕的主意。 记忆非常便宜。

FWIW,如果你是在一家制造商的PC上运行,而这家厂商喜欢在内存几率上多次充电,那么Windows Vista有一个预测性的分页算法可以很好地运行(也许是OS唯一做得很好的事情)。