GC原理

一、垃圾回收概念

对内存中不再会被使用的对象进行清除,收回其占有的空间。

二、什么时候进行垃圾回收

为新对象分配内存时,Eden区没有足够空间时会发生Minor GC。

老年代没有足够空间时,会进行Full GC。

三、如何发现需要回收的对象

当某个对象没有被引用之后就应该被回收

1、引用计数法

对每个对象设置一个引用计数器 ,每增加一个对该对象的引用,引用计数器就加1,每减少一个对该变量的引用,引用计数器就减1,当引用计数器变成0时,该对象会被回收。

局限:(1)、无法处理循环引用的情况,即A引用B,B引用A的情况。(2)引用计数器会随着引用的减少和增加做相应的操作,对系统性能有一定影响。

2、Tracing(追踪)算法

现在垃圾回收机制都使用根搜索算法,把所有的引用关系看作一张图,根集(root set)作为图的起点,从根集开始,寻找可达的对象,找到可达的对象后继续寻找这个对象的引用对象,当所有的可达的或间接可达的对象寻找完毕,剩余的则被认为是不可达的游离对象,即无用的对象。

根集对象(GC Roots):所谓根集就是正在执行的 Java 程序可以访问的引用变量的集合(包括局部变量、参数、类变量)。具体有:

常量引用的对象(方法区)

静态变量引用的对象(方法区)

方法中局部变量引用的对象(栈)

本地方法栈中引用的变量(native方法)

三、垃圾回收算法

java垃圾回收器中没有该算法

2、mark-sweep(标记-清除)算法

标记阶段:从GC Roots开始扫描,对存活(可达)对象进行标记,未被标记的对象就是要进行垃圾回收的对象

清除阶段:再扫整个空间中未被标记的对象,进行回收。

该方法不移动对象,仅仅回收未标记的对象,再对象存活较多的情况下效率极高。

容易产生内存碎片,过多的内存碎片会导致为大对象分配空间时无法找到足够的空间,再次触发垃圾回收动作

3、mark-compact(标记-整理)算法

在标记-清除算法上做了改进,标记阶段与标记-清除算法一致,在清除阶段,它先将所有存活对象上移动到一片集中的内存空间,然后清除掉该片内存以外的失效对象。

解决了内存碎片的问题。

移动对象需要额外消耗

4、copying(复制)算法(java新生代使用)

将堆内存分为使用区和空闲区,当使用区用完后,就进行一次扫描标记,将还存活的对象复制到空闲区,然后对使用区进行一次清理。此时空闲去和使用区角色互换。

简单、高效、没有内存碎片

内存利用率只有一半

若存活对象数量居多,算法效率将大大降低

5、Generational(分代)算法(Java堆使用)

根据对象生命周期长短将其进行分块,根据每块的特点,使用不同的回收算法。

新生代:存放年轻对象或者经历垃圾回收次数不多的对象的堆空间。存在大量需要被回收的对象。采用复制算法,因为复制的对象比较少

新生代的内存又按照8:1:1的比例划分为Eden和Survivor0,Survivor1。大部分对象在 Eden 区生成,回收时先将 Eden 区中存活的对象复制到一个 Survivor0 区中,然后清空 Eden 区。当这个 Survivor0 区也存放满时,则将 Eden 区和 Survivor0 区的存活对象复制到另一个 Survivor1 区,然后清空 Eden 和 Survivor0 区,此时 Survivor0 区是空的,然后将 Survivor0 区和 Survivor1 区交换,即保持 Survivor1 区为空,如此往复。

当 Survivor1 区也满了,就将存活对象直接存放到老年代。若是老年代也满了就会触发一次 Full GC,也就是新生代、老年代都进行回收。

新生代发生的 GC 也叫做 Minor GC,MinorGC 发生频率比较高(不一定等 Eden 区满了才触发)。

老年代:在年轻代中经历了多次垃圾回收后仍然存活的对象,到达一定次数后就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。所以每次回收都只回收少量对象,一般使用的是 Mark - Compact 算法。

一般来说,大对象会被直接分配到老年代,所谓的大对象是指需要大量连续存储空间的对象,最常见的一种大对象就是大数组。

老年代内存比新生代也大很多,当老年代内存满时触发 Major GC 即 Full GC,发生的频率比较低。

持久代(Permanent Generation):在堆区之外还有一个就是持久代(Permanent Generation),它用于存放静态文件,如 class 类、常量、方法描述等。持久代的回收主要回收两部分内容:废弃的常量和无用的类。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些 class,例如 Hibernate 等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。

在方法区进行垃圾回收一般”性价比”较低, 因为在方法区主要回收两部分内容: 废弃常量和无用的类. 回收废弃常量与回收其他年代中的对象类似, 但要判断一个类是否无用则条件相当苛刻:
该类所有的实例都已经被回收, Java堆中不存在该类的任何实例;
该类对应的Class对象没有在任何地方被引用(也就是在任何地方都无法通过反射访问该类的方法);
加载该类的ClassLoader已经被回收.
但即使满足以上条件也未必一定会回收, Hotspot VM还提供了-Xnoclassgc参数控制(关闭CLASS的垃圾回收功能). 因此在大量使用动态代理、CGLib等字节码框架的应用中一定要关闭该选项, 开启VM的类卸载功能, 以保证方法区不会溢出.

6、分区方法

将整个堆空间划分为连续的不同小区间,每一个小区间都独立使用,独立回收。

算法优点是:可以控制一次回收多少个小区间

通常,相同的条件下,堆空间越大,一次GC所需的时间就越长,从而产生的停顿时间就越长。为了更好的控制GC产生的停顿时间,将一块大的内存区域分割成多个小块,根据目标的停顿时间,每次合理的回收若干个小区间,而不是整个堆空间,从而减少一个GC的停顿时间。

参考:

垃圾回收机制 —— 整理介绍

JVM初探:内存分配、GC原理与垃圾收集器

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值