Java垃圾回收机制(GC算法简述)

引用计数算法

  • 判断对象的引用数量来决定对象是否可以被回收
  • 每个对象实例都有一个引用计数器,被引用则+1,完成引用-1
  • 任何引用计数为0的对象实例被当作垃圾
    优点:执行效率高,程序执行受影响小
    缺点:无法检测出循环引用的情况,导致内存泄漏

可达性分析算法

判断对象的引用链是否可达来决定对象是否可以被回收(理论基础:离散数学图论)
可以作为ROOT的对象

  • 虚拟机栈中引用的对象
  • 方法区中常量引用的对象

标记-清除算法

  • 标记:从根集合进行扫描,对存活的对象进行标记 (可达算法实现)
  • 清除:对堆内存从头到尾进行线性遍历,回收不可达对象内存
    缺点:造成碎片化

复制算法

  • 分为对象面和空闲面
  • 对象面在对象面上创建
  • 存活的对象从对象面复制到空闲面
  • 将对象所有对象内存清除
    适用于对象存活率低的场景
    解决了碎片化问题
    顺序分配内存,简单高效

标记-整理算法

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

避免了内存的不连续性
不要设置两块内存互换
适用于对象成活率极高的场景

分代收集算法(组合)

按照对象生命周期的不同划分区域以采用不同的垃圾回收算法
目的:提高JVM的回收效率

jvm的新生代、老年代、永久代,元空间(jdk1.8以后元空间取代永久代)

在这里插入图片描述
在这里插入图片描述
新生代分为三个区域,一个Eden区和两个Survivor区,它们之间的比例为(8:1:1),这个比例也是可以修改的
新生代中对象的特点是:很快就会被GC回收掉的或者不是特别大的对象
在这里插入图片描述
1.Eden区
Eden区位于Java堆的年轻代,是新对象分配内存的地方,由于堆是所有线程共享的,因此在堆上分配内存需要加锁。而Sun JDK为提升效率,会为每个新建的线程在Eden上分配一块独立的空间由该线程独享,这块空间称为TLAB(Thread Local Allocation Buffer)。在TLAB上分配内存不需要加锁,因此JVM在给线程中的对象分配内存时会尽量在TLAB上分配。如果对象过大或TLAB用完,则仍然在堆上进行分配。如果Eden区内存也用完了,则会进行一次Minor GC(young GC)。
2.Survival from to
Survival区与Eden区相同都在Java堆的年轻代。Survival区有两块,一块称为from区,另一块为to区,这两个区是相对的,在发生一次Minor GC后,from区就会和to区互换。在发生Minor GC时,Eden区和Survivalfrom区会把一些仍然存活的对象复制进Survival to区,并清除内存。Survival to区会把一些存活得足够旧的对象移至年老代。
3.年老代
年老代里存放的都是存活时间较久的,大小较大的对象,因此年老代使用标记整理算法。当年老代容量满的时候,会触发一次Major GC(full GC),回收年老代和年轻代中不再被使用的对象资源。
在新生代每进行一次垃圾收集后,就会给存活的对象“加1岁”,当年龄达到一定数量的时候就会进入老年代(默认是15,可以通过-XX:MaxTenuringThreshold来设置)
4. 永久代(Permanent Generation ):即JVM的方法区。在这里存放着一些被虚拟机加载的类信息(别忘了还有动态生成的类)的静态文件,这就导致了这个区中的东西比老年代和新生代更不容易回收。
5. 元空间(Metaspace):(降低FULL GC的频率)
从JDK 8开始,Java开始使用元空间取代永久代,元空间并不在虚拟机中,而是直接使用本地内存。
那么,默认情况下,元空间的大小仅受本地内存限制。当然,也可以对元空间的大小手动的配置。

1. 什么时候会触发Minor GC(从年轻代空间(包括 Eden 和 Survivor 区域)回收内存)?

  • Eden区域满了,或者新创建的对象大小 > Eden所剩空间

  • CMS设置了CMSScavengeBeforeRemark参数,这样在CMS的Remark之前会先做一次Minor
    GC来清理新生代,加速之后的Remark的速度。这样整体的stop-the world时间反而短

  • Full GC的时候会先触发Minor GC

2. 什么时候会触发Full GC(Full GC是清理整个堆空间—包括年轻代和永久代)?

  • Minor GC后存活的对象晋升到老年代时由于悲观策略的原因,有两种情况会触发Full GC, 1种是之前每次晋升的对象的平均大小 >老年代剩余空间
  • 1种是Minor GC后存活的对象超过了老年代剩余空间。这两种情况都是因为老年代会为新生代对象的晋升提供担保,而每次晋升的对象的大小是无法预测的,所以只能基于统计,1个是基于历史平均水平,一个是基于下一次可能要晋升的最大水平。这两种情况都是属于promotion failure
  • CMS失败,发生concurrent mode failure会引起Full GC,这种情况下会使用Serial Old收集器,是单线程的,对GC的影响很大。concurrent mode failure产生的原因是老年代剩余的空间不够,导致了和gc线程并发执行的用户线程创建的大对象(由PretenureSizeThreshold控制新生代直接晋升老年代的对象size阀值)不能进入到老年代,只要stop the world来暂停用户线程,执行GC清理。可以通过设置CMSInitiatingOccupancyFraction预留合适的CMS执行时剩余的空间
  • 新生代直接晋升到老年代的大对象超过了老年代的剩余空间,引发Full GC。注意于promotion failure的区别,promotion failure指的是Minor GC后发生的担保失败
  • Perm永久代空间不足会触发Full GC,可以让CMS清理永久代的空间。设置CMSClassUnloadingEnabled即可
  • System.gc()引起的Full GC,可以设置DisableExplicitGC来禁止调用System.gc引发Full GC
  • 使用RMI进行RPC或管理JDK应用(每一小时执行一次Full Gc)

Stop-the-World

  • JVM由于要执行GC而停止了应用程序的执行
  • 任何一种GC算法都会发生
  • 多数GC优化通过减少Stop-The-World发生的时间来提高程序性能

Safepoint(安全点)

  • 分析过程中对象引用关系不会发生变化的点
  • 产生安全点的地方:方法调用;循环跳转;异常跳转
  • 安全点数量要适中(太少GC会等待很长时间,太多会增加程序运行负荷)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值