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/ReservedCodeCacheSizeCodeCache 区初始(最大)大小
-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:+UseCMSCompactAtFullCollectionFullGC 之后做压缩
-XX:CMSFullGCBeforeCompaction多少次 FullGC 之后做压缩。
-XX:CMSInitiatingOccupancyFractionOld 占多大比例触发 FullGC
-XX:+UseCMSInitiatingOccupancyOnlyOld 占多大比例触发 FullGC,是否动态可调。
-XX:+CMSScavengeBeforeRemarkFull 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 常用的收集器组合

新生代老年代说明
SerialSerial Old
SerialCMSCMS 退化 Serial Old
ParNewCMSCMS 对 Old 区进行垃圾回收,启用它时默认对 Young 区使用 ParNew GC。
ParNewSerial Old
Parallel ScavengeSerial Old
Parallel ScavengeParallel Old
G1G1

对老年代使用 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
jstackjstack 线程状态,结合 top -H -p \ 查看线程。
jmap -dump:format=b,file=heap.hprof导出内存映像文件,使用 mat 进行查看。

参考资料