Java GC

垃圾判定

  • 没有被其他对象引用

判定算法

引用计数算法

  • 判断对象的引用数量
  • 每个对象都有一个引用计数器,被引用+1,完成引用-1
  • 任何引用计数为0的对象实例可以被当作垃圾收集

优点:执行效率高,程序执行受影响小

缺点:无法检测出循环引用,导致内存泄露(引用计数永不为0)

可达性分析算法

  • 通过判断对象的引用链是否可达来决定对象是否被回收

access

GC Root对象

  • 虚拟机栈中引用的对象
  • 方法区中常量引用的对象
  • 方法区中类静态属性引用的对象
  • 本地方法栈中JNI(Native方法)的引用对象
  • 活跃线程的引用对象

垃圾回收

垃圾回收算法

标记-清除算法(Mark & Sweep)

  • 标记:从根集合扫描,对存活对象进行标记
  • 清除:对堆内存从头到尾进行线性遍历,回收不可达对象

sweep

  • 缺点:碎片化严重

复制算法(Copying)

  • 内存被分为对象面和空闲面
  • 对象在对象面创建
  • 对象面用完,将存活的对象复制到空闲面
  • 将对象面所有对象清理

copy

  • 解决碎片化问题
  • 顺序分配内存,简单高效
  • 适合存活率低的场景(不适合老年代)

标记-整理算法(Compacting)

  • 标记:从根集合扫描,对存活对象进行标记
  • 清除:移动所有存活对象,按照内存地址重新依次排列,将末端内存地址以后的内存全部回收

compact

  • 避免了碎片化
  • 不用设置两块内存空间
  • 适用于存活率高的场景(老年代)

分代收集算法(Generational Collector)

  • 回收算法组合拳
  • 按照对象生命周期不同划分区域采取不同算法

gcold

  • jdk8以后取消永久代

gcnow

  • 年轻代存活率低,采用复制算法
  • 老年代存活率高,采用标记清除/整理算法

Minor GC

  • 年轻代GC
  • 复制算法
  • 频繁

年轻代分为两个区域

  • Eden
  • 两个Survivor:From,To
  • 8:1:1

young

对象在Eden出生,确定存活转移到s0

y1

清除所有eden区对象,并将存活对象年龄设置为1

y2

Eden重新加载对象,重复上述过程到s2,年龄+1

y2

清空Eden和s0

y2

周而复始

y2

年龄达到某值,进入老年区

-XX:MaxTenuringThreshold //可调整此年龄

年轻代溢出对象也会转移至老年代

晋升到老年代的对象:

  • 经理多数Minor GC,年龄超过一定数值
  • Survivor中放不下的对象
  • 新生成的大对象

-XX:+PretenuerSizeThreshold

调优参数

-XX:SurvivorRatio //Eden,Survivor比例,默认8:1

-XX:NewRatio //年轻代与老年代比例,默认1:2

Full GC

  • 老年代存放周期较长的对象
  • 采用标记清理/整理算法GC
  • 速度慢
  • 频率低

触发条件:

  • 老年代空间不足
  • 永久代空间不足(JDK7以后取消)
  • CMS GC出现promotion/concurrent mode fail
  • Minor GC晋升老年代平均大小大于剩余空间
  • 调用System.gc() 显式但不强制
  • 使用RMI进行RPC或管理的JDK应用,一小时执行一次

年轻代垃圾回收器

Stop-the-World

  • JVM由于要执行GC而停止应用程序的执行
  • 任何GC算法都会发生
  • 减少stop-the-world实现GC优化

Safepoint

  • GC时不再产生垃圾
  • 分析过程中对象引用关系不会发生变化的点
  • 到达安全点程序才会暂停
  • 多在方法调用,循环跳转,异常跳转等
  • 安全点数量适中

JVM运行模式

  • server,启动慢运行快
  • client,启动快运行慢

java -version

垃圾收集器关系

collector

Serial收集器

-XX:+UseSerialGC //复制算法

  • 单线程收集,GC时,必须暂停所有工作线程
  • 简单高效,client默认年轻代收集器

ParNew收集器

-XX:+UserParNewGC //复制算法

  • 多线程收集
  • 单核执行效率不及Serial,多核下有优势

Parallel Scavenge收集器

-XX:+UserParallelGC //复制算法

  • 吞吐量=运行用户代码时间/(运行用户代码时间+GC时间)
  • 关注吞吐量,忽视用户线程卡顿
  • Server模式下默认的年轻代收集器

老年代垃圾回收器

Serial Old收集器

-XX:+UseSerialOldGC //标记整理算法

  • 单线程收集,必须停止工作线程
  • 简单高效

Parallel Old收集器

-XX:+UseParallelOldGC //标记整理算法

  • 多线程,吞吐量优先

CMS收集器

-XX:UseConcMarkSweepGC //标记清除算法

  • 初始标记:stop-the-world,JVM停顿,时间短
  • 并发标记:并发追溯标记,程序不停顿
  • 并发预处理:查找执行并发标记时晋升老年代的对象,不停顿
  • 重新标记:暂停虚拟机,扫描CMS堆中剩余对象
  • 并发清理:清理垃圾对象,程序不卡顿
  • 并发重置:重置CMS收集器的数据结构

特点:边产生边回收、碎片化严重

Garbage First

  • 并行和并发
  • 分代收集
  • 空间整合
  • 可预测停顿

1、将整个Java堆内存划分为多个大小相等的Region

2、年轻代老年代不再物理隔阂

gf

收集器结合情况

gclink

Q

  • Object.finalize()是否与C++析构函数作用相同

不同

析构函数调用确定,finalize()不确定,也就是还未运行完已经被GC

  • Java中的强引用、软引用、弱引用和虚引用

强引用:

Object obj = new Object();

1、即使抛出OutOfMemoryError也不会回收该对象

2、可以通过设置为null来弱化作用,使其被回收

软引用:

SoftReference softRef=new SoftReference(str);

1、对象有用但非必须状态

2、只有当内存不足,GC才会回收

3、可用来实现高速缓存

弱引用

WeakReference weakRef=new WeakReference(str);

1、比软引用更弱

2、GC时被回收

3、GC优先级低,所以被回收概率也不大

虚引用:

1、形同虚设

2、任何时候都可能被回收

3、跟踪对象被GC的活动,起哨兵作用

4、必须和引用队列ReferenceQueue联合使用

ref

四种引用的关系:

ref4

  • Copyrights © 2019-2020 Rex