如何分析java中的内存碎片?

时间:2009-08-10 06:33:48

标签: java memory-management

我们的服务器经历了几分钟的滞后。可能它们是由“停止世界”垃圾收集引发的。但我们使用并发标记和扫描GC(-XX:+ UseConcMarkSweepG),所以,我认为,这些暂停是由旧一代的内存碎片触发的。

如何分析老一代的内存碎片?它有什么工具吗?

每小时都会发生一次。大多数时间他们大约20秒,但有时 - 几分钟。

7 个答案:

答案 0 :(得分:6)

查看Java文档中的“java -X ...”选项以打开GC日志记录。这将告诉您是否正在收集旧的或新的一代,以及收集的时间。

暂停“几分钟”听起来非同寻常。您确定不仅仅是在堆大小太小的情况下运行,还是在物理内存不足的机器上运行?

  • 如果你的堆太接近满了, 将再次触发GC 再次,导致您的服务器 花费大部分CPU时间 GC。这将显示在GC中 日志。

  • 如果在计算机上使用大堆 物理内存不足, 完整的GC可能会导致 你的机器“捶打”,消费 大部分时间疯狂地移动虚拟 来自光盘的内存页面。您 可以用系统来观察 监测工具;例如通过观看 控制台输出“vmstat 5” 典型的UNIX / Linux系统。

<强>后续

与OP的观点相反,开启GC日志记录不太可能对性能产生明显的影响。

Oracle网站上的Understanding Concurrent Mark Sweep Garbage Collector Logs页面应该有助于解释GC日志。

最后,OP的结论是,这是一个“碎片化”的问题是不可能的,并且(IMO)没有得到他提供的证据片段的支持。这很可能是其他的东西。

答案 1 :(得分:3)

对于低级别监控,您需要使用此-XX:PrintFLSStatistics=1(或以更多阻塞成本将其设为2)。它没有记录,偶尔会给你一些统计数据。不幸的是,由于不同的原因,它在大多数应用程序中并不是非常有用,但它至少是有用的。

你应该可以看到例如

Max Chunk Size: 215599441

并将其与此

进行比较
Total Free Space: 219955840

然后根据平均块大小和块数来判断碎片。

答案 2 :(得分:0)

我已经使用YourKit来解决这类问题。

答案 3 :(得分:0)

维塔利,存在碎片问题。    我的观察: 如果对象的小尺寸经常更新,那么在这种情况下它会产生大量垃圾。虽然CMS收集这些对象占用的内存,但这个内存是碎片化的。现在Mark-Sweep-Compact线程进入画面(停止世界)并试图压缩这个碎片化的内存导致长时间停顿。

与此相反,如果对象大小较大,则会产生较少碎片的内存和
Mark-Swap-Compact缩短了这个内存所需的时间。这可能会导致吞吐量降低,但可以帮助您减少GC压缩造成的长时间暂停。

答案 4 :(得分:0)

这是一个难以找到的问题。由于我花了一些时间在一个系统中找到并证明这一点,让我列出发生这种情况的场景

  • 我们坚持使用Java 6,它没有任何压缩的垃圾收集器
  • 我们的应用程序做了太多GC主要是年轻一代的收藏和一些老一代的收藏
  • 我们的堆大小是一个非常大的主要问题(我们减少了,但是我们的应用程序在太多的字符串和集合上花了很多钱)

表明的问题是我们系统中只有一个特定算法运行缓慢;其余所有同时运行的,运行正常。这排除了Full GC;我们还使用jstat和其他j **工具来检查GC,线程转储+尾随GC日志。

从jstack线程转储中花费了一段时间,我们可以了解哪个代码块真的在减速。所以怀疑是堆积碎片。

为了测试我写了一个简单的程序,它初始化了两个List一个ArrayList和一个LinkedList,并添加了导致调整大小的操作。我可以通过REST句柄执行此测试。 通常没有太大区别。但是在一个零碎的堆中,时间上有明显的差异;使用ArrayList重新调整大集合变得非常慢,而不是使用Linked列表。这些时间被记录下来,除了一个零碎的头部之外没有其他解释。

使用Java 7,我们转向G1GC,以及GC调优和改进应用程序的大量工作;在这里堆压缩要好得多,它可以处理更大的堆,但我想任何超过16克的堆都会让你落到你真正不想去的地方 - GC suckage :)

答案 5 :(得分:-1)

要了解Vitaly如何处理此问题,请参阅Understanding Concurrent Mark Sweep Garbage Collector Logs

答案 6 :(得分:-4)

Java中没有内存碎片;在GC运行期间,内存区域被压缩。

由于您没有看到高CPU利用率,因此也没有GC运行。所以别的东西必然是你问题的原因。以下是一些想法:

  • 如果您的应用程序的数据库位于其他服务器上,则可能存在网络问题

  • 如果您运行Windows并且已映射网络驱动器,则其中一个驱动器可能会锁定您的计算机(再次出现网络问题)。 Unix上的NFS驱动器也是如此。检查系统日志中是否存在网络错误。

  • 计算机是否将大量数据交换到磁盘?由于CPU util低,问题的原因可能是应用程序被交换到磁盘并且GC运行强制它返回到RAM。如果您的服务器没有足够的实际RAM来将整个Java应用程序保存在RAM中,则需要很长时间。

此外,其他进程可以强制应用程序退出RAM。检查实际内存利用率和交换空间使用情况。

要了解GC日志的输出,this post可能有所帮助。

[编辑]我仍然无法理解“低CPU”和“GC档位”。这两者通常相互矛盾。如果GC停止运行,您必须看到100%的CPU使用率。如果CPU空闲,则其他东西阻止GC。你有超载finalize()的对象吗?如果一个finalize阻止,GC可以永远。