JVM之垃圾收集算法

生活中,最使人疲惫的往往不是道路的遥远,而是心中的郁闷;最使人痛苦的往往不是生活的不幸,而是希望的破灭;最使人颓废的往往不是前途的坎坷,而是自信的丧失;最使人绝望的往往不是挫折的打击,而是心灵的死亡。所以我们要有自己的梦想,让梦想的星光指引着我们走出落漠,走出惆怅,带着我们走进自己的理想。

导读:本篇文章讲解 JVM之垃圾收集算法,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

垃圾回收机制概述

Java中一个显著的特点就是引入了垃圾回收机制,而c++需要自己进行内存管理,Java不再需要考虑内存管理。垃圾回收可以有效的防止内存泄露,有效的使用空闲的内存。

内存泄露是指该内存空间使用完毕之后未回收,在不涉及复杂数据结构的一般情况下,Java 的内存泄露表现为一个内存对象的生命周期超出了程序需要它的时间长度,有时也将其称为“对象游离”。

手动GC回收

不可达的对象并不会马上就会被直接回收。

public class GcTest {
    public static void main(String[] args) {
        GcTest gcTest = new GcTest();
        //gcTest=null; gc才会执行.
        gcTest=null;
        System.gc();
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("Gc执行回收.....");
    }
}
Gc执行回收.....

进程已结束,退出代码为 0

finalize作用

finalize()方法在垃圾收集器将对象从内存中清除出去前,做必要的清理工作。也就是在垃圾收集器删除对象之前对这个对象的调用。

该方法是由垃圾收集器在确定这个对象没有被引用时对这个对象的调用。由于它是在Object类中定义的,所有的类都继承了它。子类覆盖finalize()方法以整理系统资源或者执行其他清理工作。

垃圾回收机制算法

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

标记-清除(Mark-Sweep)算法分为标记和清楚两个阶段

堆中所有的对象都会被扫描一遍,从而才能确定需要回收的对象,比较耗时。
遍历所有的 GC Roots,然后将所有 GC Roots 可达的对象标记为存活的对象

标记就是根据特定的算法(如:引用计数算法,可达性分析算法等)标出内存中哪些对象可以回收,哪些对象还要继续用

标记指示回收,那就直接回收;标记指示对象还能用,那就留下

清除掉被标记需要回收的对象,释放出对应的内存空间

整个过程:首先标记出所有需要回收的对象,然后统一回收所有被标记点对象
在这里插入图片描述

缺点:

标记和清除的两个过程效率都不高

清除之后内存会产生大量碎片

标记清除后产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作

优化:

使用标记-压缩算法解决内存碎片问题。

标记-压缩/整理算法(Mark-Compact)

标记压缩法在标记清除算法基础之上做了优化,首先对所有可达对象做一次标记,然后将堆中所有的存活对象压缩到堆内存的一端,这样就会在堆中另一端留出很大的一块空闲区域,最后进行垃圾清理。(老年代使用的就是标记压缩法)

让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
在这里插入图片描述

标记-复制算法(Mark-Copying)

原理:

将原有的内存空间分为两块(S0和s1按容量分成大小相等的两块),每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,然后清除正在使用的内存块中的所有对象,交换两个内存的角色,完成垃圾回收。

过程:

把堆内存分成两个大小相同的区域,在任何时刻,只有其中的一个区域被使用,当这个区域内存使用完毕,此时垃圾回收器会中断程序的执行,通过遍历的方式把所有活动的对象复制到另外一个区域中,在复制的过程中它们是紧挨着布置的,从而可以消耗内存碎片。当复制过程结束后程序会紧着运行,直到这块区域被使用完,继续上述方式进行垃圾回收。

简言之:

1.将内存划分为两块相等的区域,每次只使用其中一块

2.当其中一块内存使用完,将还存活的对象复制到另外一块,然后把已经使用过的内存空间一次清除掉。

优点:

确保回收后的内存空间是没有空间碎片的。

复制算法适合用于新生代,因为在新生代,垃圾对象通常会多于存活对象,复制算法的效果会比较好。

缺点:

复制收集算法在对象存活率较高时就要进行较多的复制操作,效率将会变低

可使用的内存降为原来一半,对于指定大小的堆来说,需要两倍大小的内存空间,同时由于在内存调整的过程中要中断当前执行的程序,从而降低了程序的执行效率。

在这里插入图片描述

分代收集算法

根据内存中对象的存活周期不同,将内存划分为几块,java虚拟机中一般把内存划分为新生代和年老代。

当新创建对象时一般在新生代中分配内存空间,当新生代垃圾收集器回收几次之后仍然存活的对象会被移动到年老代内存中,当大对象在新生代中无法找到足够的连续内存时也直接在年老代中创建。

对于新生代和老年代来说,新生代回收频率很高,但是每次回收耗时很短,而老年代回收频率较低,但是耗时会相对较长,所以应该尽量减少老年代的GC.所以应该根据垃圾回收对象的特性,使用合适的算法回收。比如,新生代使用复制算法,老年代使用标记压缩回收算法。

分代收集算法就是根据对象的年代,采用不同算法来收集。

Young区:每次GC都有大量对象死去,存活的很少,常采用复制算法,只需要拷贝很少的对象。

Old区:常采用标整或者标清算法,Old区对象存活时间比较长,复制来复制去没必要,不如做个标记再清理

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/136873.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!