文章目录
Pre
性能优化 - 案例篇:池化对象_Commons Pool 2.0通用对象池框架
性能优化 - 案例篇: 19 条常见的 Java 代码优化法则
- 引言
- JVM 内存区域划分:堆、虚拟机栈、程序计数器、本地方法栈、元空间、直接内存
- GC Roots 与可达性分析:原理、包含的根集合
- 引用类型等级:强、软、弱、虚引用及应用场景
- 分代垃圾回收:年轻代/老年代、G1 算法简介与参数
- 小结
引言
在性能优化中,JVM 理论知识不可或缺。逃逸分析、非捕获型 Lambda 优化等高级技巧,都建立在对 JVM 内部运行原理的深刻理解之上。
接下来我们将系统梳理:JVM 内存区域划分、GC Roots、引用类型与分代垃圾回收。
1. JVM 内存区域划分
JVM 将运行时内存划分为六大区域:
1.1 堆(Heap)
- 作用:对象实例与数组的主要分配区,也是垃圾回收器重点回收目标。
- 特点:可通过 -Xmx/-Xms 参数调整大小。
1.2 Java 虚拟机栈(JVM Stack)
-
线程私有:每个线程创建时分配,随线程销毁。
-
栈帧结构:局部变量表、操作数栈、动态连接、返回地址。
-
执行示例:
public void test() { int a = 1; a++; }
方法调用时生成栈帧,操作数栈执行 ILOAD/ICONST/ IINC 等字节码指令。
1.3 程序计数器(PC Register)
- 线程私有:记录下一条待执行字节码地址,恢复线程执行位置。
- 无 OOM:唯一规范中不抛 OutOfMemoryError 的区域。
1.4 本地方法栈(Native Method Stack)
- 与 JVM 栈类似,用于 native 方法,HotSpot 将二者合并管理。
1.5 元空间(Metaspace)
-
演变:JDK8 之后替代永久代(PermGen),分配在 堆外 ,默认无上限。
-
要点
-
元空间不在堆上分配;
-
字符串常量池:
- JDK1.6 及之前在永久代;
- JDK1.7 移入堆;
- JDK1.8 继续在堆中。
-
1.6 直接内存(Direct Memory)
- API:通过 ByteBuffer.allocateDirect 申请,可用 -XX:MaxDirectMemorySize 控制。
- 区别:“本地内存”泛指所有非 JVM 分配内存;直接内存指特定 API 操作。
2. GC Roots 与可达性分析
垃圾回收采用 可达性分析(Reachability Analysis),先标记可达对象,再回收剩余对象。GC Roots 包括:
- 当前线程栈帧中的局部变量与方法参数引用;
- 所有已加载类的 Class 对象;
- 静态变量引用;
- 运行时常量池中的引用类型常量;
- JVM 内部数据结构引用(如 HotSpot 的 Universe);
- 同步监控对象(调用 wait()/notify() );
- JNI Handle(全局与局部句柄)。
入口大约有三个:线程、静态变量和 JNI 引用
3. 四种引用级别
- 强引用(Strong):最普通引用,只要存在即不被回收。
- 软引用(Soft):仅当内存不足时才回收,常用于缓存。
- 弱引用(Weak):GC 时无论内存状况均会回收,常用于非关键缓存。
- 虚引用(Phantom):用于在对象回收后做额外处理(如 Cleaner );不会影响对象生存周期。
4. 分代垃圾回收
基于 弱代假设:大多数对象短命,少部分长寿。
4.1 年轻代(Young Generation)
- 包含 Eden、Survivor From、Survivor To,比例默认为 8:1:1(-XX:SurvivorRatio)。
- 对 Eden 区满触发 Minor GC,活跃对象在 Survivor 空间之间来回复制,达到阈值后晋升老年代。
4.2 老年代(Old Generation)
- 对长期存活或大对象进行回收(Major/Full GC),停顿时间更长。
4.3 G1 垃圾回收器简介
-
将堆划分为多个小区域(Region),综合年轻代和老年代概念。
-
根据 -XX:MaxGCPauseMillis 目标,动态选择回收区域;
-
主要参数:
- MaxGCPauseMillis:最大停顿目标;
- G1HeapRegionSize:Region 大小;
- InitiatingHeapOccupancyPercent:触发并发标记的堆占用比例。
小结
- 区分 JVM 内存布局与 Java 内存模型(JMM,主存与工作内存)。
- 本文覆盖:堆、虚拟机栈、PC 寄存器、本地方法栈、元空间、直接内存;GC Roots;引用类型;分代回收与 G1。