Java 垃圾回收机制 GC 及常用的垃圾回收算法

在 Java 中,所有的对象都是要存在内存中的(也可以说内存中存储的是一个个对象),因此我们将内存回收,也可以叫做死亡对象的回收;

GC 回收的目标是堆上的对象;而栈中的局部变量会跟随栈帧的声明周期结束,静态变量生命周期是整个程序,因此都不需要 GC 回收;

GC 回收操作可分为两步——找到垃圾对象和回收垃圾对象

1. 找到垃圾对象

1)引用计数(Python 采用这种方式)

为 new 出来的对象,单独安排一块空间,保存计数器,表示有多少个引用指向该对象,当一个对象的引用计数器为 0,就可以视为垃圾了;

引用计数存在的问题:

a. 比较浪费内存

b. 存在循环引用问题,下面是个循环引用的示例:

public class Main {
    public Main t;
    public static void main(String[] args) {
        Main a = new Main();
        Main b = new Main();
        a.t = b;
        b.t = a;
        a = null;
        b = null;
    }
}
2)可达性分析(Java 采用这种方式)

有一个或一组线程,周期性的扫描代码中的所有对象(从一些特定的对象出发,尽可能的进行访问的遍历,把所有能够访问到的对象都标记为 "可达",未被标记的对象则为 "垃圾");这些特定的对象称为 "GC Roots",从这些节点开始向下搜索,搜索走过的路径称之为 "引用链",当一个对象到GC Roots 没有任何的引用链相连时(说明 GC Roots到这个对象不可达),则该对象是不可用的,需要被回收;

在 Java 中,可作为 GC Roots 的对象包含以下几种

  1. 虚拟机栈(栈帧中的本地变量表)中引用的对象;
  2. 方法区中类静态属性引用的对象;
  3. 方法区中常量引用的对象;
  4. 本地方法栈中 JNI(Native 方法)引用的对象;

2. 回收垃圾

1)标记清除

该算法分为 "标记" 和 "清除" 两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象;

缺点:

a. 效率问题:标记和清除这两个过程的效率都不高;

b. 空间问题:标记清除后会产生大量不连续的内存碎片,导致剩余的内存空间不连续,这可能会导致以后在程序运行中,需要分配较大对象时,无法找到足够连续的内存;

2)复制算法

"复制" 算法是为了解决 "标记,清理" 的效率问题。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这块内存需要进行垃圾回收时,会将此区域还存活着的对象复制到另⼀块上面, 然后再把已经使用过的内存区域一次清理掉。(通过复制的方式,把有效的对象归类到一起,再统一释放剩下的空间)这样做的好处是每次都是对整个半区进行内存回收,不会出现内存碎片问题,此算法实现简单,运行高效。

缺点:

有效对象非常多时,复制拷贝的开销会非常大;

3)标记整理:

此算法的标记过程仍与 "标记,清除" 过程一致,但后续步骤不是直接对可回收对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉该端边界以外的内存;

2    4    6    

如图,若 1,3,5是无效的垃圾,则经该算法整理后会变为:

246      

 缺点:搬运的开销仍很大

4)分代回收算法(Java 采取的方式)

分代回收算法可以说是上述三种方法的结合体,通过区域划分,实现不同区域和不同的垃圾回收策 略,从而实现更好的垃圾回收

该算法把 Java 堆分为新生代和老年代;其中新生代放新建的对象,老年代存放在经新生代的多次GC 之后还存活的对象;

新生代又分为 伊甸区和幸存区(又分为大小相等的两块);

在新生代中,每次垃圾回收都有大批对象死去,只有少量存活,因此采用复制算法;

在伊甸区中,使用复制算法将有效对象复制到幸存区,然后将整个伊甸区释放;

在幸存区中,每次只使用其中一块,也使用复制算法,将活过 GC 的有效对象,拷贝到另一块幸存区,然后将该快幸存区释放;

而老年代中对象存活率高、没有额外空间对它进行分配担保,就采用 "标记-清理" 或者 "标记-整理" 算法;

当某个对象已经在幸存区存活很多轮 GC 之后,JVM 就认为该对象短时间内不会释放,会把该对象拷贝的老年代, GC 仍会使用标记算法扫描老年代,但是扫描老年代的频率会比扫描新生代的频率低很多;

  • 29
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Rcnhtin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值