快速答案
堆由年轻代和老年代组成。所以当你进行堆转储时,内容包括两者。堆转储的统计数据应该是分开的。尝试删除命令中的二进制部分并以纯文本查看它。您将看到配置摘要,然后是每个代的详细信息。另一方面,-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 -heap
获得了JVM的“非二进制”统计信息(99%的老年代已满)。我希望我能够对只有老年代的堆进行堆转储,因为我想比较一些对象(内存缓存)的内存大小随时间的变化情况,但由于Young经常被GC,所以我可以在几个后续的转储中获得更大或更小的大小...所以很难比较。不过我会尝试启用GC统计信息。 - Matthieu Napoli