JAVA基础:GC

一、垃圾的检索标记

1.标记算法

被其他对象引用的被标记为存活,没有被其他对象引用的被认为是垃圾,垃圾回收会回收那些标记为垃圾的内存空间。

2.方法一:引用计数算法

(1)即通过判断对象的引用数量来决定对象是否可以被回收。针对每个对象实例,都有一个引用计数器,被引用则+1,完成引用则-1。而对于任何引用计数为0的对象实例,都可以被当作垃圾收集。

(2)优点:执行效率高,程序执行受影响较小
(3)确定:无法检测出循环引用的情况,导致内存泄露。例如:即父对象引用子对象,同时子对象引用父对象,则不可能回收。代码实例:两种对象之间相互交互,其中对象

public class GCObject {
    public GCObject childObject;
}

程序中应用相互引用,则会导致这个对象从来不会被回收

public class test2 {
    public static void main(String[] args) {
        GCObject object1=new GCObject();
        GCObject object2=new GCObject();
        
        object1.childObject=object2;
        object2.childObject=object1;
    }
}


3.方法二:可达性算法

(1)定义
通过判断对象的引用链是否可达来决定对象是否可以被回收

(2)作为GC Root对象范围

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

 

二、垃圾回收算法

1.标记-清除算法(Mark and Sweep)

(1)实现

标记:基于可达性算法,从根集合进行扫描,对存活的对象进行标记
清除:对堆内存从头到尾进行线性遍历,回收不可达对象内存。最后,将标记的对象进行清楚,从而方便下一次垃圾回收。

(2)过程
 
 
(3)问题
碎片化:内存直接回收,导致内存分散,没有一个完整的内存。下一次如果申请较大的内存,会提前触发下一次垃圾回收

2.复制算法(Copying)

(1)原理

分为对象面和空闲面,一般是按照1:1分配。新对象创建在对象面上,当对象面中块内存使用完成,则将对象面中的对象全部复制到空闲面,重新排列,同时将对象面所有对象内存清除。

(2)过程

(3)优势

  • 解决碎片化问题,因为复制区按照顺序重新分配内存
  • 顺序分配内存,简单高效
  • 适用于对象存活率低的场景

3.标记整理算法(Compacting)

(1)定义

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

(2)回收过程

 

(3)优势和适用场景

  • 避免内存的不连续
  • 不用设置两块内存互换,增加内存空间利用率
  • 适用于存活率高的场景,比如老年代

4.分代回收算法(Generational Collectior)

(1)定义

针对堆中不同区域,采用不同垃圾回收算法的组合。按照对象生命周期的不同,将对象放入堆中不同的区域;对于不同的区域,采用不同的垃圾回收算法。从而,可以提高JVM的垃圾回收效率

(2)堆内存分配

JDK1.8之前,以及之后,如下图
 
参考:https://www.cnblogs.com/secbro/p/11718987.html

(3)年轻代

作用:尽可能快速地收集掉那些生命周期短的对象。年轻代分为:Eden区和两个Survivor区,由于新生代中存活对象较少,所以采用复制算法,简单高效。具体结构如下图

具体回收过程:

新建对象,先进入Eden区。当Eden区被对象填满时,就会执行Minor GC。第一次执行,把所有存活下来的对象转移到其中一个survivor from区。第二次及以上执行,Minor GC同样会检查survivor from区存活下来的对象,并把survivor from+Eden中存活对象转移到另一个survivor to区,同时清空survivor from+Eden区。然后from和to区域互换。这样在一段时间内,总会有一个空的survivor区。

与此同时,每存活一次,对象的记录就会加1,当大于-XX:MaxTenuring Threshold(默认15),则会进入老年代。

(4)年轻代进入老年代

  • 经历一定Minor次数依然存活的对象
  • Minor gc后,Survivor区中存放不下的对象
  • 新生成的大对象(-XXx:+PretenuerSizeThreshold),判断如果超过这个size,则直接进入老年代
  • 动态年龄判断,大于等于某个年龄的对象超过了survivor空间一半,大于等于某个年龄的对象直接进入老年代

年轻代进入老年代调优参数

  • -XX:SurvivorRatio:Eden和其中一个Survivor(from或者to)的比值,默认8:1
  • -XX:NewRatio:老年代和年轻代内存大小的比例
  • -XXxMaxTenuring Threshold:对象从年轻代晋升到老生代经过GC次数的最大阈值

(5)老年代:存放生命周期较长的对象

采用算法原理:年代中对象较多,并且没有可以担保的内存区域,所以采用标记清理算法或者标记整理算法

具体回收算法:当老年代回收,会使用Full GC(minor GC针对新生代和Major GC针对老年代),Full GC比Minor GC慢,但执行频率低

触发full gc条件:
(a)老年代空间不足
(b)永久代空间不足(1.8之前)
(c)CMS GC时出现promotion failed,concurrent mode failure
(d)Minor GC晋升到老年代的平均大小大于老年代的剩余空间
(e)调用System.qc(),只是提醒虚拟机需要回收,具体回收时机,需要JVM决定
(f)使用RMI进行RPC或者管理JDK应用,每小时执行1次Full GC

(6)年轻代和老年代回收实例

-》1.7之前

-》1.8之后将最初的永久代取消,替换为元空间

5.常见垃圾回收器

5.1 CMS垃圾收集器

(1)使用:-XX:+UseConcMarkSweepGC,使用标记-清除算法

(2)步骤

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

如下图

(3)CMS存在问题 

-》CMS采用的基础算法是标记-清除。所以CMS不会整理、压缩堆空间。经过CMS收集的堆会产生空间碎片。虽然节约了垃圾回收的停顿时间,但也带来堆空间的浪费。

-》需要更多的CPU资源,为了让应用程序不停顿,CMS线程和应用程序线程并发执行,这样就需要有更多的CPU,单纯靠线程切换是不靠谱的。


5.2 G1(Garbage First)垃圾收集器

(1)使用G1收集器:-XX:+UseG1GC,复制+标记-整理算法

(2)G1相较于CMS的改进点

        不同于其他的分代回收算法,G1最大的特点是引入分区的思路,弱化了分代的概念,合理利用垃圾收集各个周期的资源,解决了其他收集器甚至CMS的众多缺陷。每块区域既有可能属于O区(老年代)、也有可能是Y区(新生代),且每类区域空间可以是不连续的(对比CMS的O区和Y区都必须是连续的)。

(3)G1特点

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

(4)适合G1的场景

目前CMS还是默认首选的GC策略,在以下场景使用G1更适合:

  • 服务端多核CPU、JVM内存占用较大的应用(至少大于4G)
  • 应用在运行过程中产生大量内存碎片、需要经常压缩空间
  • 想要更可控、可预期的GC停顿周期:防止高并发应用雪崩现象

(5)G1对比CMS的区别

  • G1在压缩空间方面有优势
  • G1通过将内存空间分成区域(Region)的方式避免内存碎片问题。而Eden/Survivor/Old分别是多个region的逻辑集合,物理上内存地址并不连续,所以在内存使用效率上来说更灵活

  • G1可以通过设置预期停顿时间(Pause Time)来控制垃圾收集时间避免应用雪崩现象,可驾驭度。G1是可以设定GC暂停的target 时间的,根据预测模型选取性价比收益更高,且一定数目的Region作为CSet,能回收多少便是多少。
  • G1在回收内存后会马上同时做,合并空闲内存的工作;而CMS默认是在STW(stop the world)的时候做
  • G1同时回收老年代和年轻代,而CMS只能回收老年代,需要配合一个年轻代收集器。

CMS在old gc的时候会回收整个Old区,而对于G1来说没有old gc的概念,而是区分Fully young gc和Mixed gc,其中,Fully young gc对应年轻代的垃圾回收,Mixed gc混合了年轻代和部分老年代的收集。因此每次收集肯定会回收年轻代,老年代根据内存情况可以不回收或者回收部分或者全部(这种情况应该是可能出现)。

  • SATB算法在remark阶段延迟极低以及借助RememberedSet的实现可以不做全堆扫描(G1对大堆更友好)以外,最重要的是可驾驭度
     

参考:https://blog.csdn.net/hellozhxy/article/details/80144419

三、参考

1.https://blog.csdn.net/tonytfjing/article/details/44278233

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值