美文网首页
JVM 内存结构和垃圾回收

JVM 内存结构和垃圾回收

作者: 王龙江_3c83 | 来源:发表于2019-03-19 08:50 被阅读0次

    1. JVM 内存结构

    1228818-20180430152017807-1294956408.png

    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 常用参数

    参数 解释
    -Xint 解释执行,运行时将 class 翻译成机器码。
    -Xcomp 编译执行,第一次使用时进行编译,保存在 JVM 中。
    -Xmixed 以方法为单位,将多次调用的代码翻译成机器码。
    参数 解释
    -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,默认值为 15),则会晋升到老年代。-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

    3.5.5 如何选择垃圾回收器

    • 优先调整堆的大小让服务器自己来选择。

    • 如果内存小于 100M,使用串行收集器。

    • 如果是单核并且对停顿时间没要求,选用串行垃圾回收器或让 JVM 自己选。

    • 如果允许停顿时间超过 1 秒,选用并行垃圾回收器或让 JVM 自己选。

    • 如果响应时间,并且不能超过 1 秒,选用并发垃圾回收器。

    4. 内存监控

    4.1 查看 Java 默认参数

    参数 解释
    java -XX:+PrintFlagsFinal -version 查看 JVM 参数。
    java -XX:+PrintCommandLineFlags -version 查看 JVM 默认垃圾回收器。

    4.2 查看 JVM 运行时参数

    参数 解释
    jstat 查看 JVM 统计信息,包括类装载、垃圾回收和 JIT 编译信息。
    jstat -gc 查看 JVM 内存对分配和使用状况,GC 的次数和时间。
    jstat -class 查看 JVM 类装载信息。
    jstat -compiler 查看 JVM 编译信息。
    jinfo -flag <name> <pid> 查看正在运行的 JVM 的参数值。
    jinfo -flag <pid> Non-deafault VM flags,查看被修改的 JVM 的参数值。
    jps 打印 java 进程号和进程名。
    jps -v 打印 java 进程的详细信息。
    jstack jstack 线程状态,结合 top -H -p <pid> 查看线程。
    jmap -dump:format=b,file=heap.hprof <pid> 导出内存映像文件,使用 mat 进行查看。

    实战代码

    参考资料

    相关文章

      网友评论

          本文标题:JVM 内存结构和垃圾回收

          本文链接:https://www.haomeiwen.com/subject/cxuwmqtx.html