从这个输出中,我们可以看到所加载类的名称(ClassName)、每个类所占据的字节(KlassBytes)、每个类的实例所占据的字节(InstBytes)、每个类中方法的数量(MethodCount)、字节码所占据的空间(ByteCodes))等等。
需要注意的是,在Java 8中,这个诊断命令需要Java进程使用‑XX:+UnlockDiagnosticVMOptions选项启动。
jcmd 33984 GC.class_stats 33984:
GC.class_stats command requires -XX:+UnlockDiagnosticVMOptions
在Java 9中,该诊断命令不需要-XX:+UnlockDiagnosticVMOption。
OutOfMemoryError: Native Memory原生内存出现OutOfMemoryError的一些示例如下所示:
因为没有足够交换空间(swap space)所引起的OutOfMemoryError:
# A fatal error has been detected by the Java Runtime Environment:
#
# java.lang.OutOfMemoryError: requested 32756 bytes for ChunkPool::allocate. Out of swap space?
#
# Internal Error (allocation.cpp:166), pid=2290, tid=27 # Error: ChunkPool::allocate
因为没有足够进程内存所导致的OutOfMemoryError
# A fatal error has been detected by the Java Runtime Environment:
#
# java.lang.OutOfMemoryError : unable to create new native Thread
这些错误清楚地表明JVM不能分配原生内存,这可能是因为进程本身消耗掉了所有的原生内存,也可能是系统中的其他进程在消耗原生内存。在使用“pmap”(或其他原生内存映射工具)监控原生堆的使用之后,我们可以恰当地配置Java堆、线程数以及栈的大小,确保有足够的空间留给原生堆,如果我们发现原生堆的使用在持续增长,最终会出现OutOfMemoryError,那么这可能提示我们遇到了原生内存的泄露。
64位JVM上的原生堆OutOfMemoryError在32位JVM中,进程大小的上限是4GB,所以在32位Java进程中更容易出现原生内存耗尽的情况。但是,在64位JVM中,对内存的使用是没有限制的,从技术上讲,我们可能认为永远也不会遇到原生堆耗尽的情况,但事实并非如此,原生堆遇到OutOfMemoryErrors的情况并不少见。这是因为64位JVM默认会启用一个名为CompressedOops的特性,该特性的实现会决定要将Java堆放到地址空间的什么位置。Java堆的位置可能会对原生内存的最大容量形成限制。在下面的内存地图中,Java堆在8GB的地址边界上进行了分配,剩下了大约4GB留给原生堆。如果应用需要在原生内存分配大量空间,超出了4GB的话,即便系统还有大量的内存可用,它依然会抛出原生堆OutOfMemoryError。