万字概览 Java 虚拟机 (4)

Klass Metaspace 通过 -XX:+UseCompressedClassPointers 参数和 -XX:+UseCompressedOops 参数来启用,默认是启用的。其大小通过 -XX:CompressedClassSpaceSize 参数来控制,默认是 1G,但这块空间的初始大小是无法设定的。从这一点也可以看出,JVM 研发团队考虑以堆外更大空间来缓解方法区的 OOM。这块空间也可以不启用,Klass 将直接存放到 NoKlass Metaspace 中。特别的是,如果 -Xmx 大于 32G,JVM 也必然不会启用这块空间。Klass Metaspace 分配在紧邻 Heap 的 Native Memory 中。

-XX:+UseCompressedClassPointers 这个参数实际要表达的是启用压缩指针。在 32bit VM 中,存在于对象头 MarkWord 中的类型指针占 4 字节,而在 64bit VM 中占用 8 字节。当开启压缩指针后,64bit VM 中的类型指针会被压缩到 4 字节,从而达到节省内存空间的目的。

NoKlassMetaspace 除了在 Klass Metaspace 空间不存在时用于存储非压缩 Klass 指针外,还包括运行时常量池、CodeCache 等区域。

运行时常量池

在每一份字节码文件中都存在一个 Class 常量池,主要保存的是编译期生成的符号引用和字面量。字面量包括文本字符串、基本类型的值和使用 final 声明的常量;符号引用包括类和方法的全限定名、字段(方法)的名称和描述符。

当一个类被 JVM 加载后,JVM 就会将它 Class 常量池中的内容放到运行时常量池中,同时将符号引用替换为直接应用,对于有文本字符串字面量的,会在 StringPool 中查找是否存在同样字符串以确保在运行时常量池中引用的字符串和在 StringPool 中的是一致的。

Class 常量池和 Runtime 常量池的关系

The runtime constant pool is an implementation-specific data structure that maps to the constant pool in the class file。

CodeCache

随着方法的不断被调用,JVM 也会统计各个方法被调用的次数。当某个方法调用频率比较高时,JVM 会使用 C1 JIT 编译器将其进行编译,编译后的代码就保存在 CodeCache 中。这样,之后再调用这个方法就不会在解释执行,提高了执行效率。

如果被 C1 编译后的方法中还存在更热点的方法,JVM 就会使用 C2 编译器对其进行更高层级的编译。C2 比 C1 更能编译出更高的执行效率,但编译时对时间和资源的消耗也更大,所以 JVM 采用解释执行、C1 编译、C2 编译这种「分层编译」方式来平衡效率和资源耗用。

分层编译使用参数 -XX:+TieredCompilation 开启,默认是开启的。

使用 -Xint 参数强制 JVM 对所有代码都解释执行,无论是否出现热点方法,都不进行编译;-Xcomp 参数强制 JVM 对所有代码都编译执行。

ReservedCodeCacheSize 参数用于设置 CodeCache 的大小,默认开启分层编译的情况下是 240M。如果 CodeCache 空间不足,会将一半最早编译的代码放到一个 Old 列表中,如果在一定时间内 Old 列表中的方法没有被调用就会从 CodeCache 中清除,表示这已经不是热点方法了。

MetaspaceSize 和 MaxMetaspaceSize

如果配置过 Java8 之前版本的 JVM,你一定使用过 -XX:PermSize 和 -XX:MaxPermSize 两个参数来控制永久代的大小。当你看到 -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize 两个参数的时候,也很大可能会认为他们与永久代配置的两个参数的作用是对应的,但实际上只能参数名称造成的误解。

MetaspaceSize 主要用于控制触发 MetaspaceGC 的阈值,即当 Metaspace 使用内存达到指定大小时触发一次 MetaspaceGC。但是 MetaspaceGC 并不是一个真正的 GC,它只是 Full GC 下的一种 GC Cause,在 GC 日志中表示为 Full GC (Metadata GC Threshold)。通过参数指定的 MetaspaceSize 的值是第一次触发 Full GC 的阈值,这个值会在每次 GC 完成后动态增大或者缩小。

MaxMetaspaceSize 用于控制 Metaspace 的最大可使用内存,默认数值非常大,基本上可以当做是无限大。由于 Metaspace 使用的是 Native Memory,为了避免因类存泄露而导致 Native Memory 被无限制消耗,通常还是建议设定一个合适的值。另外,MaxMetaspaceSize 只用于限制 Metaspace 可使用内存的上限(Max Memory),并不会在 JVM 已启动就分配这么大的内存空间。

关于 java.lang.management.MemoryUsage 四个值的说明

init:表示 JVM 启动时向 OS 报告的需要的内存(并不会实际分配),约等于 Xms

max:最大可向 OS 申请的内存空间,约等于 `Xmx

committed:已向 OS 实际申请到的、可以直接使用的内存空间,空间不足时 JVM 继续向 OS 申请,直到 max

used:表示已实际使用的内存空间,小于等于 committed

垃圾回收算法和垃圾回收器 对象存活判断算法

目前主流的存活判断算法有两种,引用计数器法和可达性分析算法,几乎所有主流的 JVM 都采用后者来判断对象是否存活。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zygjjy.html