JVM-内存结构和垃圾回收
优质
小牛编辑
132浏览
2023-12-01
1.1 程序计数器
JVM 支持多线程同时执行,每个线程都有自己的程序计数器,线程正在执行 Java 代码,则存放正在执行的指令地址,如果正在执行 C 代码(本地方法),则为空。
1.2 虚拟机栈
线程私有,每个方法创建一个栈帧,用于存储局部变量表(this、参数列表、局部变量)、操作数栈(将下一个指令入栈,执行时出栈)、动态链接、方法出口等信息。方法从调用到执行完成对应栈帧的入栈到出栈,线程内串行。
1.3 本地方法栈
为虚拟机所使用的 C++ Native 方法服务。
1.4 堆
存放对象实例。
1.4.1 Young
- S0+S1 同样大小,相同时间内,S0 和 S1 只有一个有数据,另一个为空。
- Eden 小于阈值的新生对象区。
1.4.2 Old
1.5 元数据区
存放被虚拟机加载的类信息,包括常量、静态变量、即时编译器编译后代码等。 存放 Class、Package、Method、Field、字节码、常量池、符号引用。 - CCS(压缩类空间) 堆中对象都有一个指向自己 Class 对象的指针,每个 64 位指针长度为 64 位,如果使用 32 位指针,Class 文件存放于 CCS 中。
- CodeCache JIT 即时编译后的代码和 JVM 执行的 JNI Native 代码。如果使用
-Xint
解释执行,则不会生成 CodeCache。1.6.1 运行时常量池
方法区的一部分,存放类加载后生成的字面量和符号引用。
1.7 常用参数
-X 参数
参数 | 概念 |
---|---|
-Xint | 解释执行,运行时将 class 翻译成机器码。 |
-Xcomp | 编译执行,第一次使用时进行编译,保存在 JVM 中。 |
-Xmixed | 以方法为单位,将多次调用的代码翻译成机器码。 |
-XX 参数
Boolean 类型 -XX:[+-]\
非 Boolean 类型 -XX:\=\
参数 | 解释 |
---|---|
-Xms=-XX:InitialHeapSize | 最小堆内存大小 |
-Xmx=-XX:MaxHeapSize | 最大堆内存大小 |
-XX:NewSize -XX:MaxNewSize | 最小/最大新生代大小 |
-XX:NewRatio | 新生代(eden+s*s)和老年代的比值。 |
-XX:SurvivorRatio | 两个 Survivor 区和 eden 的比值 |
-XX:MetaspaceSize/MaxMetaspaceSize | 元数据区大小。 |
-XX:+UseCompressedClassPointers | 使用压缩类指针 |
-XX:+UseCompressedClassSpaceSize | 使用压缩类指针 |
-XX:InitialCodeCacheSize/ReservedCodeCacheSize | CodeCache 区初始(最大)大小 |
-Xss=-XX:ThreadStackSize | 设置每个线程的堆栈大小。 |
3. GC
3.1 垃圾判断算法
3.1.1 引用计数
3.1.2 可达性分析
选定活动对象(类加载器、Thread、虚拟机栈的本地变量表和本地方法栈作的变量、静态成员和常量)作为一个 GC Roots,然后跟踪引用链条。如果一个对象和 GC Roots 间不可达,即可认为是可回收对象。
3.2 性能指标
术语 | 解释 |
---|---|
最长停顿时间 | 垃圾收集器做垃圾回收时中断应用执行的时间的最大值。XX:MaxGCPauseMills 最大停顿时间。 |
吞吐量 | 花在垃圾收集时间和花在应用时间的占比 XX:GCTimeRatio ,垃圾收集时间占比:1/1+n |
3.3 垃圾收集算法
算法 | 解释 | 优点 | 缺点 |
---|---|---|---|
复制算法 | 将堆内存按照容量分成大小相同的两块,每次只使用其中一块。当一块的内存用完时,将存活者的数据复制到另一块,然后将使用过的内存空间清除掉。 | 简单高效 | 空间利用率低。 |
标记清除算法 | 首先标识出所有需要回收的对象,然后统一回收。 | 导致内存碎化,标记和清除效率不高。 | |
标记-整理算法 | 为了避免内存碎片化,在清理过程将对象移动。 | 没有内存碎片 | 整理内存耗时。 |
3.4 分带垃圾回收
- Young 区采用复制算法,Old 采用标记清除或标记整理。对象在 Young 区分配,大部分对象生命周期非常短,采用复制算法效率非常高。Old 区存放对象生命周期长,垃圾较少。
- 对象分配到 Egen 区(大对象直接进入 Old 区,大于
-XX:PretenureSizeThreshold
),当 Eden 区域空间占用达到一定阈值时,触发 Minor GC,将被引用的对象分配到 Survivor 区域,没有被引用对象被回收,存活对象年龄标记为 1。 - 经过一次 Minor GC,Eden 空闲,当 Eden 再次达到阈值,触发 Minor GC,Egen 区域的存活的对象和 From 区对象,被复制到 to 区,年龄加 1 。第二次发生多次,直到有对象年龄达到阈值(
-XX:MaxTenuringThreshold
),则会晋升到老年代。-XX:TargetSurvivorRatio
存活对象比例,计算该比例下对象的平均年龄,取其和-XX:MaxTenuringThreshold
的最小值,作为晋升年龄。 - 老年代的 GC 称为 Major GC。
3.5 垃圾回收器
3.5.1 类型
- 串行:SerialGC、SerialOldGC
- 并行:Parallel ScanvengeGC、Parallel Old
- 并发: CMS、G1
3.5.2 参数
|参数|区域|线程|算法|类型| |------|------|------|------|------| |-XX:+UseSerialGC|新生代|单线程|复制算法|串行| |-XX:+UseSerialOldGC|老年代|单线程|标记整理|串行| |-XX:+UseParallerGC|新生代|多线程|复制算法| |-XX:+UseParallerOldGC|老年代|多线程|标记整理| |-XX:+UseConcMarkSweepGC|老年代|多线程|标记清除| |-XX:+UseParNewGC|新生代|新生代|多线程|标记整理| |-XX:+UseG1GC|老年代和新生代|多线程|标记整理|3.5.1 串行收集器
3.5.2 并行收集器(Server 端默认)
3.5.2.1 使用场景
吞吐量优先:多垃圾收集线程并行工作,此时用户线程处于等待状态。适合科学计算、后台处理等弱交互场景。3.5.2.2 参数
|参数|解释| |------|------| |-XX:+UseParallerGC(默认)|对新生代使用并行垃圾回收器,对老年代使用 Ps MarkSweep(类似于穿行垃圾回收器)。| |-XX:+UseParallerOldGC|对老年代使用并行收集器。| |-XX:ParallelGCThreads|并行的垃圾回收线程数,默认小于 8 核,线程数等于核数;大于 8核,等于 5/8 * 核数。| |-XX:MaxGCPauseMills|最大停顿时间。| |-XX:GCTimeRatio|垃圾收集时间占比:1/(1+n)| |-Xmx|优先满足最大停顿时间和吞吐量,如果堆大小不能满足需求。| |-XX:YoungGenerationSizeIncrement|| |-XX:TenuredGenerationSizeIncrement||3.5.3 并发收集器(响应时间优先)
垃圾回收线程和用户线程同时执行(但不一定是并行,可能交替执行),垃圾回收线程在执行的时候不会停顿用户程序的运行。适合对响应时间有要求(低于 1秒 )的场景,比如 Web。
3.5.3.1 CMS
3.5.3.1.1 参数
参数 | 解释 |
---|---|
-XX:+UseConcMarkSweepGC | 对老年代使用 CMS 垃圾回收算法。 |
-XX:+UseParNewGC | 对新生代使用 ParNew 垃圾回收算法。 |
-XX:ConcGCThreads | 并发的 GC 线程数。 |
-XX:+UseCMSCompactAtFullCollection | FullGC 之后做压缩 |
-XX:CMSFullGCBeforeCompaction | 多少次 FullGC 之后做压缩。 |
-XX:CMSInitiatingOccupancyFraction | Old 占多大比例触发 FullGC |
-XX:+UseCMSInitiatingOccupancyOnly | Old 占多大比例触发 FullGC,是否动态可调。 |
-XX:+CMSScavengeBeforeRemark | Full GC 之前做 YGC |
-XX:+CMSClassUnloadingEnabled | 启动回收 Perm 区。 |
3.5.3.1.2 过程
- 初始标记 Root,STW
- 并发标记
- 并发预清
- 重新标记:STW
- 并发清除
- 并发重置
3.5.3.1.3 缺点
- CPU 敏感
- 浮动垃圾:并行标记时,应用线程进行内存分配。
- 空间碎片
3.5.3.2 G1
3.5.3.2.1 参数
参数 | 解释 |
---|---|
-XX:+UseG1GC | 对老年代和新生代使用 G1 垃圾回收算法。 |
-XX:+InitiatingHeapOccupancyPercent | 堆占有率达到这个值时,触发 global concurrent marking,默认 45%。 |
-XX:+G1HeapWastePercent | 堆占有率达到这个值时,触发 global concurrent marking,默认 45%。 |
大内存(6GB)低停顿时间(0.5 s)小,新生代和老年代。
3.5.3.2.2 YoungGC
同上,大对象(大于 REGION 大小的 50%,进入 H 区)。
3.5.3.2.3 MixedGC
3.5.4 常用的收集器组合
新生代 | 老年代 | 说明 |
---|---|---|
Serial | Serial Old | |
Serial | CMS | CMS 退化 Serial Old |
ParNew | CMS | CMS 对 Old 区进行垃圾回收,启用它时默认对 Young 区使用 ParNew GC。 |
ParNew | Serial Old | |
Parallel Scavenge | Serial Old | |
Parallel Scavenge | Parallel Old | |
G1 | G1 |
对老年代使用 Ps MarkSweep(类似于穿行垃圾回收器)。
3.5.5 如何选择垃圾回收器
- 优先调整堆的大小让服务器自己来选择。
- 如果内存小于 100M,使用串行收集器。
- 如果是单核并且对停顿时间没要求,选用串行垃圾回收器或让 JVM 自己选。
- 如果允许停顿时间超过 1 秒,选用并行垃圾回收器或让 JVM 自己选。
- 如果响应时间,并且不能超过 1 秒,选用并发垃圾回收器。
4. 内存监控
3.1 查看 Java 默认参数
参数 | 解释 |
---|---|
java -XX:+PrintFlagsFinal -version | 查看 JVM 参数。 |
java -XX:+PrintCommandLineFlags -version | 查看 JVM 默认垃圾回收器。 |
3.2 查看 JVM 运行时参数
参数 | 解释 |
---|---|
jstat | 查看 JVM 统计信息,包括类装载、垃圾回收和 JIT 编译信息。 |
jstat -gc | 查看 JVM 内存对分配和使用状况,GC 的次数和时间。 |
jstat -class | 查看 JVM 类装载信息。 |
jstat -compiler | 查看 JVM 编译信息。 |
jinfo -flag \ \ | 查看正在运行的 JVM 的参数值。 |
jinfo -flag \ | Non-deafault VM flags,查看被修改的 JVM 的参数值。 |
jps | |
jstack | jstack 线程状态,结合 top -H -p \ 查看线程。 |
jmap -dump:format=b,file=heap.hprof | 导出内存映像文件,使用 mat 进行查看。 |
参考资料
- 《深入理解Java虚拟机》
- 查看JVM使用的默认的垃圾收集器
- jvm 监控工具
- MAT内存分析工具-独立版安装