JVM与GC调优(四)-对象的生命周期和垃圾回收GC篇

本文最后更新于:2024年4月22日 下午

Java对象的生命周期,对象从创建过程,到内存中分配方式,如何分配以及何时进入老年代相关。JVM垃圾回收

对象的生命周期

对象创建的流程

创建流程

对象创建过程

对象内存的分配方式

内存分配的方法有两种:不同垃圾收集器不一样

  • 指针碰撞(Bump the Pointer)

    内存地址是连续的(新生代),Serial 和ParNew 收集器

  • 空闲列表(Free List)

    内存地址不连续(老年代),CMS 收集器和 Mark-Sweep 收集器

对象内存分配的安全问题

在并发情况下, 可能出现正在给对象A分配内存,指针还没来得及修改,对象B又同时使用了原来的指针来分配内存的情况

在JVM中有两种解决办法

  • CAS 是乐观锁的一种实现方式。虚拟机采用 CAS 配上失败重试的方式保证更新操作的原子性。
  • TLAB本地线程分配缓冲(Thread Local Allocation Buffer即TLAB):为每一个线程预先分配一块内存

对象怎样才会进入老年代?

对象的分配对象情况如下

  • 新生代:新对象大多数都默认进入新生代的Eden区。伊甸园(希腊神话)

  • 老年代(四种情况):

    • 存活年龄太大,默认超过15次【-XX:MaxTenuringThreshold

    • 动态年龄判断,MinorGC之后,发现Survivor区中的一批对象的总大小大于了这块Survivor区

      的50%,那么就会将此时大于等于这批对象年龄最大值的所有对象,直接进入老年代

    • 大对象直接进入老年代,前提是Serial和ParNew收集器

    • MinorGC后,存活对象太多无法放入Survivor

空间担保机制

当新生代无法分配内存的时候,我们想把新生代的老对象转移到老年代,然后把新对象放入腾空的新生代。此种机制我们称之为内存担保

对象的内存布局

堆内存中,一个对象在内存中存储的布局可以分为三块区域

  • 对象头(Header)Java对象头占8byte。如果是数组则占12byte。因为JVM里数组size需要使用

    4byte存储

    • 标记字段MarkWord
      • 用于存储对象自身的运行时数据,它是synchronized实现轻量级锁和偏向锁的关键
      • 默认存储:对象HashCode、GC分代年龄、锁状态等等信息
      • 锁标志位的变化,存储数据发生变化
    • 类型指针KlassPoint
      • 是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例
      • 开启指针压缩存储空间4byte,不开启8byte
      • JDK1.6+默认开启
    • 数组长度
      • 如果对象是数组,则记录数组长度,占4个byte,如果对象不是数组则不存在
    • 对齐填充
      • 保证数组的大小永远是8byte的整数倍
  • 实例数据(Instance Data)

    • 生成对象的时候,对象的非静态成员变量也会存入堆空间
  • 对齐填充(Padding

    • JVM内对象都采用8byte对齐,不够8byte的会自动补齐

如何访问一个对象

  • 句柄:稳定,对象被移动只要修改句柄中的地址
  • 直接指针:访问速度快,节省了一次指针定位的开销

JVM垃圾收集器

概述

什么是垃圾?

在内存中,没有被引用的对象就是垃圾

如何找到这个垃圾?

主要是2种:引用计数法根可达算法

  • 引用计数法(Reference Counting

    • 引用计数算法不能解决循环引用问题
  • 根可达算法(GCRoots Tracing

    • 通过一系列的名为GCRoot的对象作为起始点,从这些节点开始向下搜索,搜索所走过的

      路径称为引用链(Reference Chain,当一个对象到GCRoot没有任何引用链相连时,则证明此对象是不可用的,也就是不可达的

可作GCRoots的对象

  • 虚拟机栈中,栈帧的本地变量表引用的对象
  • 方法区中,类静态属性引用的对象
  • 方法区中,常量引用的对象
  • 本地方法栈中,JNl引用的对象、

回收过程

垃圾对象在死亡前至少经历两次标记

第一次标记:如果对象可达性分析后,发现没有与GC Roots相连接的引用链,那它将会被第一次标记

第二次标记:第一次标记后,接着会进行一次筛选。筛选条件:此对象是否有必要执行finalize() 方法。在 finalize() 方法中没有重新与引用链建立关联关系的,将被进行第二次标记

对象引用

引用分为强引用(StrongReference)、软引用(SoftReference)、弱引用(WeakReference)、虚引用(PhantomReference)四种,这四种引用强度依次逐渐减弱

如何清除垃圾?

  • Mark-Sweep 标记清除算法

  • Copying 拷贝算法

  • Mark-Compact 标记压缩算法

标记清除算法(Mark-Sweep

最基本的算法,主要分为标记清除2个阶段。首先标记出所有需要回收的对象,在标记完成后统一回收

掉所有被标记的对象

缺点

  • 效率不高标记和清除过程的效率都不高

  • 空间碎片,会产生大量不连续的内存碎片,会导致大对象可能无法分配,提前触发GC

拷贝算法(Copying

现在商业虚拟机都是采用这种收集算法来回收新生代

它将可用内存按容量划分为相等的两块,每次只使用其中的一块。当这一块的内存用完

了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉

缺点:

  • 存在空间浪费

标记整理算法(Mark-Compact

标记过程仍然与“标记-清除”算法一样,然后让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。没有空间浪费,没有内存碎片化问题

缺点:

  • 性能较低,因为除了拷贝对象以外,还需要对象内存空间进行压缩,所以性能较低。

分代回收(Generational Collection

  • 新生代,每次垃圾回收都有大量对象失去,选择复制算法
  • 老年代,对象存活率高,无人进行分配担保,就必须采用标记清除或者标记整理算法

用什么清除垃圾

两大类,串行收集器和并行收集器。有 8 种不同的垃圾回收器

  • 新生代回收器:SerialParNewParallel Scavenge

  • 老年代回收器:Serial OldParallel OldCMS

  • 整堆回收器:G1ZGC

串行收集器

Serial收集器

配置参数-XX:+UseSerialGC

特点:

  • Serial新生代收集器,单线程执行,使用复制算法
  • Serial Old老年代收集器,单线程执行,使用复制算法
  • 进行垃圾收集时,必须暂停用户线程(挂起,Safepoint)

Safepoint挂起线程的时机:

  • 循环的末尾
  • 方法返回前
  • 调用方法的call之后
  • 抛出异常的位置

Parallel Scavenge并行收集器

配置参数:-XX:+UseParallelGC

特点:

  • 吞吐量优先收集器,垃圾收集需要暂停用户线程
  • 新生代使用并行回收收集器,采用复制算法
  • 老年代使用串行收集器,采用标记-整理算法

Parallel Old收集器

配置参数: -XX:+UseParallelOldGC

特点:

  • PS(Parallel Scavenge)收集器的老年代版本
  • 吞吐量优先收集器,垃圾收集需要暂停用户线程
  • 老年代使用并行收集器,采用标记-整理算法

ParNew收集器

配置参数:-XX:+UseParNewGC或者 -XX:ParallelGCThreads=n 设置并行收集器收集时使用的并行收集线程数。一般最好和计算机的CPU相当

特点:

  • 新生代并行(ParNew),老年代串行(Serial Old
  • Serial收集器的多线程版本
  • 单核CPU不建议使用

CMS收集器

配置参数: -XX:+UseConcMarkSweepGC

特点:

  • 低延迟:减少STW对用户体验的影响
  • 并发收集,可以同时执行用户线程
  • 不会等到堆填满再收集,到达阈值就开始收集
  • 采用标记-清除算法,所以会产生内存碎片

G1(Garbage-First)收集器(JDK1.8之后)

G1是一款面向服务端应用的垃圾收集器,大内存企业配置的垃圾收集器大多都是G1

配置参数: -XX:+UseG1GC

特点:

  • 吞吐量和低延时都行的整堆垃圾收集器
  • G1最大堆内存支持64GB,最小堆内存2GB
  • 全局采用标记-整理算法收集,局部采用复制算法收集
  • 可预测的停顿

ZGC(Z Garbage Collector

在 JDK11 中引入的一种可扩展的低延迟垃圾收集器,在 JDK15 中发布稳定版

配置参数: -XX:+UseZGC

特点:

  • 并发

  • 基于 region

  • 压缩

  • NUMA 感知

  • 使用彩色指针

  • 使用负载屏障


JVM与GC调优(四)-对象的生命周期和垃圾回收GC篇
https://hyq965672903.gitee.io/posts/1382b133.html
作者
灼华
发布于
2023年3月12日
许可协议