diff --git a/notes/JVM.md b/notes/JVM.md index c2f101f9..66c3ba2f 100644 --- a/notes/JVM.md +++ b/notes/JVM.md @@ -29,7 +29,17 @@ * [7. G1 收集器](#7-g1-收集器) * [8. 七种垃圾收集器的比较](#8-七种垃圾收集器的比较) * [内存分配与回收策略](#内存分配与回收策略) + * [1. 优先在 Eden 分配](#1-优先在-eden-分配) + * [2. 大对象直接进入老年代](#2-大对象直接进入老年代) + * [3. 长期存活的对象进入老年代](#3-长期存活的对象进入老年代) + * [4. 动态对象年龄判定](#4-动态对象年龄判定) + * [5. 空间分配担保](#5-空间分配担保) * [Full GC 的触发条件](#full-gc-的触发条件) + * [1. 调用 System.gc()](#1-调用-systemgc) + * [2. 老年代空间不足](#2-老年代空间不足) + * [3. 空间分配担保失败](#3-空间分配担保失败) + * [4. JDK 1.7 及以前的永久代空间不足](#4-jdk-17-及以前的永久代空间不足) + * [5. Concurrent Mode Failure](#5-concurrent-mode-failure) * [三、类加载机制](#三类加载机制) * [类的生命周期](#类的生命周期) * [类初始化时机](#类初始化时机) @@ -394,7 +404,7 @@ Region 不可能是孤立的,一个对象分配在某个 Region 中,可以 对象的内存分配,也就是在堆上分配。主要分配在新生代的 Eden 区上,少数情况下也可能直接分配在老年代中。 -#### 1. 优先在 Eden 分配 +### 1. 优先在 Eden 分配 大多数情况下,对象在新生代 Eden 区分配,当 Eden 区空间不够时,发起 Minor GC。 @@ -403,21 +413,21 @@ Region 不可能是孤立的,一个对象分配在某个 Region 中,可以 - Minor GC:发生在新生代上,因为新生代对象存活时间很短,因此 Minor GC 会频繁执行,执行的速度一般也会比较快。 - Full GC:发生在老年代上,老年代对象和新生代的相反,其存活时间长,因此 Full GC 很少执行,而且执行速度会比 Minor GC 慢很多。 -#### 2. 大对象直接进入老年代 +### 2. 大对象直接进入老年代 大对象是指需要连续内存空间的对象,最典型的大对象是那种很长的字符串以及数组。经常出现大对象会提前触发垃圾收集以获取足够的连续空间分配给大对象。 提供 -XX:PretenureSizeThreshold 参数,大于此值的对象直接在老年代分配,避免在 Eden 区和 Survivor 区之间的大量内存复制。 -#### 3. 长期存活的对象进入老年代 +### 3. 长期存活的对象进入老年代 JVM 为对象定义年龄计数器,经过 Minor GC 依然存活,并且能被 Survivor 区容纳的,移被移到 Survivor 区,年龄就增加 1 岁,增加到一定年龄则移动到老年代中(默认 15 岁,通过 -XX:MaxTenuringThreshold 设置)。 -#### 4. 动态对象年龄判定 +### 4. 动态对象年龄判定 JVM 并不是永远地要求对象的年龄必须达到 MaxTenuringThreshold 才能晋升老年代,如果在 Survivor 区中相同年龄所有对象大小的总和大于 Survivor 空间的一半,则年龄大于或等于该年龄的对象可以直接进入老年代,无序等待 MaxTenuringThreshold 中要求的年龄。 -#### 5. 空间分配担保 +### 5. 空间分配担保 在发生 Minor GC 之前,JVM 先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果条件成立的话,那么 Minor GC 可以确认是安全的;如果不成立的话 JVM 会查看 HandlePromotionFailure 设置值是否允许担保失败,如果允许那么就会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次 Minor GC,尽管这次 Minor GC 是有风险的;如果小于,或者 HandlePromotionFailure 设置不允许冒险,那这时也要改为进行一次 Full GC。 @@ -425,23 +435,23 @@ JVM 并不是永远地要求对象的年龄必须达到 MaxTenuringThreshold 才 对于 Minor GC,其触发条件非常简单,当 Eden 区空间满时,就将触发一次 Minor GC。而 Full GC 则相对复杂,有以下条件: -#### 1. 调用 System.gc() +### 1. 调用 System.gc() 此方法的调用是建议 JVM 进行 Full GC,虽然只是建议而非一定,但很多情况下它会触发 Full GC,从而增加 Full GC 的频率,也即增加了间歇性停顿的次数。因此强烈建议能不使用此方法就不要使用,让虚拟机自己去管理它的内存。可通过 -XX:+ DisableExplicitGC 来禁止 RMI 调用 System.gc()。 -#### 2. 老年代空间不足 +### 2. 老年代空间不足 老年代空间不足的常见场景为前文所讲的大对象直接进入老年代、长期存活的对象进入老年代等,当执行 Full GC 后空间仍然不足,则抛出 Java.lang.OutOfMemoryError。为避免以上原因引起的 Full GC,调优时应尽量做到让对象在 Minor GC 阶段被回收、让对象在新生代多存活一段时间以及不要创建过大的对象及数组。 -#### 3. 空间分配担保失败 +### 3. 空间分配担保失败 使用复制算法的 Minor GC 需要老年代的内存空间作担保,如果出现了 HandlePromotionFailure 担保失败,则会触发 Full GC。 -#### 4. JDK 1.7 及以前的永久代空间不足 +### 4. JDK 1.7 及以前的永久代空间不足 在 JDK 1.7 及以前,HotSpot 虚拟机中的方法区是用永久代实现的,永久代中存放的为一些 class 的信息、常量、静态变量等数据,当系统中要加载的类、反射的类和调用的方法较多时,永久代可能会被占满,在未配置为采用 CMS GC 的情况下也会执行 Full GC。如果经过 Full GC 仍然回收不了,那么 JVM 会抛出 java.lang.OutOfMemoryError,为避免以上原因引起的 Full GC,可采用的方法为增大永久代空间或转为使用 CMS GC。 -#### 5. Concurrent Mode Failure +### 5. Concurrent Mode Failure 执行 CMS GC 的过程中同时有对象要放入老年代,而此时老年代空间不足(有时候“空间不足”是 CMS GC 时当前的浮动垃圾过多导致暂时性的空间不足触发 Full GC),便会报 Concurrent Mode Failure 错误,并触发 Full GC。 @@ -528,19 +538,19 @@ System.out.println(ConstClass.HELLOWORLD); 主要有以下 4 个阶段: -#### 1. 文件格式验证 +**(一)文件格式验证** 验证字节流是否符合 Class 文件格式的规范,并且能被当前版本的虚拟机处理。 -#### 2. 元数据验证 +**(二)元数据验证** 对字节码描述的信息进行语义分析,以保证其描述的信息符合 Java 语言规范的要求。 -#### 3. 字节码验证 +**(三)字节码验证** 通过数据流和控制流分析,确保程序语义是合法、符合逻辑的。 -#### 4. 符号引用验证 +**(四)符号引用验证** 发生在虚拟机将符号引用转换为直接引用的时候,对类自身以外(常量池中的各种符号引用)的信息进行匹配性校验。 @@ -641,15 +651,15 @@ public static void main(String[] args) {