jmap – finalizerinfo
D:\tests\GC_WeakReferences>jmap -finalizerinfo 29456 Attaching to process ID 29456, please wait...Debugger attached successfully. Server compiler detected.JVM version is 25.122-b08Number of objects pending for finalization: 10
堆转储
几乎所有的堆转储分析工具都能详细给出等待终结的对象信息。
Java VisualVM的输出
Date taken: Fri Jan 06 14:48:54 PST 2017
File: D:\tests\java_pid19908.hprof
File size: 11.3 MB
Total bytes: 10,359,516
Total classes: 466
Total instances: 105,182
Classloaders: 2
GC roots: 419
Number of objects pending for finalization: 2
java.lang.OutOfMemoryError: PermGen space
我们知道,从Java 8之后,PermGen已经移除掉了。如果读者运行的是Java 8以上的版本,那么这一小节可以直接略过。
在Java 7及以前,PermGen(“永久代,permanent generation”的缩写)用来存储类定义以及它们的元数据。在这个内存区域中,PermGen意料之外的增长以及OutOfMemoryError意味着类没有按照预期卸载,或者所指定的PermGen空间太小,无法容纳所有要加载的类和它们的元数据。
要确保PermGen的大小能够满足应用的需求,我们需要监控它的使用情况并使用如下的JVM选项进行相应的配置:
–XX:PermSize=n –XX:MaxPermSize=m
OutOfMemoryError: MetaspaceMetaSpace的OutOfMemoryError输出样例如下所示:
java.lang.OutOfMemoryError: Metaspace
从Java 8开始,类元数据存储到了Metaspace中。Metaspace并不是Java堆的一部分,它是分配在原生内存上的。所以,它仅仅受到机器可用原生内存数量的限制。但是,Metaspace也可以通过 MaxMetaspaceSize参数来设置它的大小。
如果Metaspace的使用接近MaxMetaspaceSize的最大限制,那么我们就会遇到OutOfMemoryError。与其他的区域类似,这种错误可能是因为没有足够的Metaspace,或者存在类加载器/类泄露。如果出现了后者的情况,我们需要借助诊断工具,解决Metaspace中的内存泄露。
OutOfMemoryError: Compressed class spacejava.lang.OutOfMemoryError: Compressed class space
如果启用了UseCompressedClassesPointers的话(打开UseCompressedOops的话之后,会默认启用),那么原生内存上会有两个独立的区域用来存储类和它们的元数据。启用UseCompressedClassesPointers之后,64位的类指针会使用32位的值来表示,压缩的类指针会存储在压缩类空间(compressed class space)中。默认情况下,压缩类空间的大小是1GB并且可以通过CompressedClassSpaceSize进行配置。
MaxMetaspaceSize能够为这两个区域设置一个总的提交(committed)空间大小,即压缩类空间和类元数据的提交空间。
启用UseCompressedClassesPointers之后,在GC日志中会进行采样输出。在Metaspace所报告的提交和保留(reserved)空间中包含了压缩类空间的提交和预留空间。
Metaspace used 2921K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 288K, capacity 386K, committed 512K, reserved 1048576K
PermGen和Metaspace所占据的空间可以使用Java任务控制、Java VisualVM和JConsole进行监控。GC能够帮助我们理解Full GC前后PermGen/Metaspace的使用情况,也能看到是否存在因为PermGen/Metaspace充满而导致的Full GC。
另外非常重要的一点在于确保类按照预期进行了卸载。类的加载和卸载可以通过启用下面的参数来进行跟踪:
-XX:+TraceClassUnloading –XX:+TraceClassLoading
在将应用从开发环境提升到生产环境时,需要注意应用程序有可能会被无意地改变一些JVM可选参数,从而带来不良的后果。其中有个选项就是-Xnoclassgc,它会让JVM在垃圾收集的时候不去卸载类。现在,如果应用需要加载大量的类,或者在运行期有些类变得不可达了,需要加载另外一组新类,应用恰好是在–Xnoclassgc模式下运行的,那么它有可能达到PermGen/Metaspace的最大容量,就会出现OutOfMemoryError。因此,如果你不确定这个选项为何要设置的话,那么最好将其移除,让垃圾收集器在这些类能够回收的时候将其卸载掉。
加载的类和它们所占用的内存可以通过Native Memory Tracker(NMT)来进行跟踪。我们将会在下面的“OutOfMemoryError: Native Memory”小节详细讨论这个工具。
需要注意,在使用并发标记清除收集器(Concurrent MarkSweep Collector,CMS)时,需要启用如下的选项,从而确保CMS并发收集周期能够将类卸载掉:–XX:+CMSClassUnloadingEnabled
在Java 7中,这个标记默认是关闭的,而在Java 8中它默认就是启用的。
jmap