Jmap堆转储,是否包含年轻代?

5

快速问题

jmap堆转储只包括老生代,还是包括年轻一代?


详细解释

我有两个堆转储文件(jmap -heap:format=b 9999):

  • 一个是我的服务器没有负载(没有HTTP请求)的情况下;
  • 一个是在CPU使用率为50%,负载高(基准测试)的情况下。

现在,第一个转储文件显示堆大小大于第二个(我觉得这很奇怪)。

这可能是因为年轻一代(在高负载时)经常更改,因为垃圾回收器经常运行(是的,JVM几乎满了)吗?老生代已满99%,我注意到年轻一代空间使用率变化很大。

那么这意味着我在GC执行任务后立即进行了第二个转储,这就是为什么它的大小较小。我的理解正确吗?

附加信息

Java参数:

-XX:+UseParallelGC -XX:+AggressiveHeap
-Xms2048m -Xmx4096m -XX:NewSize=64m
-XX:PermSize=64m -XX:MaxPermSize=512m
1个回答

10

快速答案

堆由年轻代和老年代组成。所以当你进行堆转储时,内容包括两者。堆转储的统计数据应该是分开的。尝试删除命令中的二进制部分并以纯文本查看它。您将看到配置摘要,然后是每个代的详细信息。另一方面,-histo只会显示堆上的所有对象,没有区别。

长篇回答

可能是第二个进程刚完成了垃圾回收。或者相反,第一个进程可能已经有一段时间没有进行完整的回收,而且内存使用率很高。在您捕获时,此应用程序/服务器是否刚刚重新启动?如果您使用像 jvisualvm 这样的工具查看空闲进程,则会看到内存分配图形上下移动,即使进程没有执行任何工作也会如此。这只是JVM在自己做事情。

通常,在老年代达到99%标记之前,您的Full GC应该启动。JVM将决定何时运行完整GC。您的年轻代将波动很大,因为这是对象创建/删除最快的地方。在运行完整GC之前,将进行许多部分GC来清除年轻代。两者之间的区别意味着您的JVM活动会暂停。看到部分GC不会损害应用程序。但是完整GC的暂停时间将停止应用程序的运行。因此,您需要尽可能地将这些最小化。

如果你正在寻找内存泄漏或只是想查看你的应用程序的GC如何工作,我建议使用启动标志打印垃圾回收统计信息。

-XX:+PrintGCDetails -verbose:gc -Xloggc:/log/path/gc.log

运行你的程序一段时间,然后使用工具加载捕获的日志来帮助可视化结果。我个人使用IBM Support Assistant Workbench中提供的Garbage Collection and Memory Visualizer。它将为您提供捕获的垃圾回收统计摘要以及一个动态图表,您可以使用它来查看应用程序中的内存行为。这不会告诉您堆中有哪些对象。

在应用程序出现问题时,如果您可以接受暂停,我建议修改您的jmap命令执行以下操作:

jmap -dump:format=b,file=/file/location/dump.hprof <pid>
使用类似MAT的工具,您可以查看关于堆中对象、泄漏事件和各种其他统计信息的所有内容和参考。 用于调整讨论的编辑
根据您的启动参数,有一些可以尝试的方法:
  • 将-Xms设置为与-Xmx相同的值。这样JVM就不必花费时间分配更多的堆。如果您的应用程序需要4GB,则立即分配全部空间。
  • 根据运行此应用程序的系统上处理器的数量,您可以设置标志-XX:ParallelGCThreads=##
  • 我还没有尝试过这个,但文档显示了一个参数-XX:+UseParallelOldGC,它显示了减少旧GC收集时间的一些可能性。
  • 尝试将新生成大小更改为堆的1/4(1024而不是64)。这可能会将太多数据强制移至旧生代。默认大小约为30%,您的应用程序配置为使用大约2%的young gen。尽管您没有配置最大大小,但我担心太多的数据会移至old gen,因为new gen太小而无法处理所有请求。因此,您必须执行更多的完整(暂停)GC以清理内存。
我确实认为,如果您如此快速地分配这么多内存,则可能存在一种代问题,即将内存过早地分配给Old generation。如果调整新gen大小不起作用,则需要通过-Xmx配置添加更多堆空间或退一步,找到确切的内存保留位置。我们之前讨论的MAT工具可以向您展示在内存中保存对象的引用。我建议先尝试1和4号建议。这将是您进行试错以获得正确值的过程。

谢谢,这是一个完整的答案。快速澄清一下:我做了一个jmap dump,并用MAT进行了分析(这就是我在问题中所指的),我还使用jmap -heap获得了JVM的“非二进制”统计信息(99%的老年代已满)。我希望我能够对只有老年代的堆进行堆转储,因为我想比较一些对象(内存缓存)的内存大小随时间的变化情况,但由于Young经常被GC,所以我可以在几个后续的转储中获得更大或更小的大小...所以很难比较。不过我会尝试启用GC统计信息。 - Matthieu Napoli
在99%的捕获中(查看MAT),是否有任何泄漏嫌疑?或者在查看对象分解时,是否有任何一个对象的实例比需要的多? - Sean
哦,我每8秒就会进行一次完整的GC(持续4秒...)。你确定当进行完整的GC时,JVM是完全停止的吗?那将是一个大问题... - Matthieu Napoli
我对我的帖子进行了编辑。请查看“编辑以进行调整讨论”。我没有看到很多可以帮助您解决Full GC问题的微调选项。此时,使用配置文件会更有用。 - Sean
两者都会暂停,只是老年代需要更长的时间。查看您启用的GC日志。您可以在那里看到所有内容的暂停时间。在我现在正在开发的应用程序中,年轻代的清理需要0.1秒,而完整的GC(在老年代)需要5-7秒。因此,显然,让用户经历许多0.1秒的暂停比让他们经历更长的完整GC更好。 - Sean
显示剩余12条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接