在Java中查找内存分配热点

16

我们的GC正在努力工作,但是有一些暂停时间需要缩短。 我们希望在调整JVM GC参数时解决一些内存分配问题。

我想知道哪些对象会导致GC过度运行:

  1. 是否有一种方法可以知道每次GC运行时哪些对象被移走?
  2. 是否有一种方法可以知道每次GC运行时哪些对象在不同区域之间移动?
  3. 是否有一种方法可以知道哪些对象在Eden区域中?

我正在广泛使用Jprofiler和Memory Analyzer。 我希望在我的staging环境中的运行应用程序中获取这些信息。


最佳选择是通过查找分配热点来最小化创建的对象。您还可以查看每个类别的保留大小和数量,以确定要集中关注哪些类别。 - Peter Lawrey
跟踪分配不是更好的想法吗? - auselen
1
我们试图找出的是哪些对象能够在年轻一代中幸存并到达Eden或Old,尽管它们是临时的。 - Zamir
5个回答

4

对于2和3,您可以在应用程序运行期间使用Oracle或OpenJDK java可执行文件的-XX:+PrintGCDetails-XX:+PrintGCTimeStamps标志。


我怎样才能查看特定对象的信息?哪些类正在被清除?它们在堆中停留了多长时间? - Zamir
不是特定的对象,而是整体对象行为,请查看本文以获取可视化工具推荐和示例用例。 - higuaro
好的,那不是问题,我知道PrintGCDetails和pringGCTimeStamps,但它们不是我要找的。 - Zamir

2

对于问题1,可以使用虚引用。这将使您在对象被垃圾收集(或可收集)时得到通知。

至于另外两个问题,我不太清楚。


2
在运行的JVM中查找内存分配可能需要很大的努力。
相反,您可以检出热点源代码并在GC或分配时添加自己的日志。编译您自己的热点并跟踪这些日志。
使用-XX:+PrintCompilation可以帮助您。 -XX:+PrintCompilation标志的输出如下所示:
1  sb   java.lang.ClassLoader::loadClassInternal  (6 bytes)     
2  b    java.lang.String::lastIndexOf  (12 bytes)
3  s!b  java.lang.ClassLoader::loadClass  (58 bytes)

国旗对应着:
b    Blocking compiler (always set for client)  
*    Generating a native wrapper    
%    On stack replacement   
!    Method has exception handlers  
s    Synchronized method    

更多细节


听起来很有趣,我会试着深入了解一下,可能会非常困难。 - Zamir

2

我们可能会尝试使用一些字节码插装来添加finalize(可能是Object),这将在日志中提供给我们这些信息。


不确定 finalize 方法是否有帮助,因为具有 finalize 方法的实例可能会或可能不会在 GC 期间被收集。这可能会严重扭曲分配/释放行为。 - Aleš

1

OldGen空间的内容:您可以查找哪些对象驻留在OldGen中。您需要在Full GC之前和之后打印直方图:-XX:+PrintClassHistogramBeforeFullGC -XX:+PrintClassHistogramAfterFullGC。由于Full GC之前执行了YoungGen收集,因此这些直方图将向您展示Old generation的内容

检测过早晋升的对象:为了确定是否有实例被提前晋升,您需要研究直方图,查看哪些类应该驻留在OldGen中,哪些类只应该出现在YoungGen中。这不能自动完成,您需要考虑每个类及其实例的目的,以确定该对象是否是临时的。

YoungGen/Eden空间的内容:我还没有找到一种方法来区分哪些对象驻留在Eden等区域。正如您可能知道的那样,jProfilerMemoryAnalyzer可以转储堆,但这不会告诉您对象驻留在哪个区域。但是,您也可以定期拍摄直方图快照,然后编写一个简单的脚本来解析输出,并告诉您哪些实例增长最快,使用:jmap -histo $pid


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