美文网首页
Java虚拟机(JVM) 之 GC

Java虚拟机(JVM) 之 GC

作者: 波波维奇c | 来源:发表于2019-03-20 22:40 被阅读0次

    其实我以前看到提到 JVM 之类的东西,就觉得这玩意太难,看不下去啊。可是要进阶吧,又是必须的啦,所以说一句 干就完了


    image

    今天就来学学 GC,想问就我一个人想到中文的滚粗吗


    image

    其实这么理解也可以啦,GC 是什么意思呢,可以大概这么理解 公厕只有这么些个,偏偏有些人占着茅厕不那啥,只好让管理员让它滚粗啦,好让其它人可以用,让世界和平。

    好了好了 回来了回来了,为什么会有 GC (回收垃圾,释放内存)呢?因为内存就这么大,用完了不回收,就一直占用着,可以用的内存就越来越小,到最后溢出了(OOM),程序也就GG了。

    那么问题来了,怎么知道这个占用着的对象是不是垃圾呢?

    一:引用计数法

    给对象添加一个引用计数器,每当有一个地方引用它时,计数器就加1,当引用失效时,计数器就减 1,任何时刻计数器为 0 的对象就是不可能再被使用的,就是垃圾了,可以回收了。(但是主流的虚拟机并不是用这种方法)

    二:可达性分析算法

    讲解这个之前,我觉得还是要先知道 Java 运行时的内存区域是怎么区分的。自行百度?哈哈
    网上找了张图:


    image

    什么意思呢?
    意思就是,这个算法呢就是通过一系列称为 “GC Roots” 的对象作为起点,从这些起点向下搜索,搜索走过的路径称为 引用链,当一个对象到 GC Roots 没有引用链的时候,我们就可以指着它的鼻子骂“垃圾”,等着被回收吧?

    那么就有人懵逼了 GC Roots 是什么玩意啊,我不知道啊

    举个我认为的例子:看过战狼2的都知道,有了中国的护照,我们就是祖国花朵(哈哈),到哪都是中国人的身份,那么国家就是我们最强的后盾。那么我们就可以把 中国理解为 GC Roots,护照就是筛选是否是中国人的 引用链,有就有中国做我们的后盾,没有就等着被回收(没被的意思....)

    那么在程序中 什么可以作为 GC Roots呢?
    • 虚拟机栈中的引用
    • 方法区中类静态属性引用的对象
    • 方法区中常量的引用对象
    • 本地方法栈中 JNI(Native) 引用的对象
    什么时候回收

    不同的虚拟机实现有着不同的 GC 实现机制,但是一般情况下每一种 GC 实现都会在以下两种情况下触发垃圾回收。
    Allocation Failure:在堆内存中分配时,如果因为可用剩余空间不足导致对象内存分配失败,这时系统会触发一次 GC。
    System.gc():在应用层,Java 开发工程师可以主动调用此 API 来请求一次 GC。

    那么这里就引出了 引用 的概念,全部有4种引用
    • 强引用:
      就是代码中直接 new 对象的引用,这种是永远不会被回收的
    • 软引用:
      用来描述一些非必须的对象,弱引用的对象 将在内存将要发生内存溢出异常之前,会被列进回收范围进行二次回收,如果回收之后内存还不够,才报溢出异常
    • 弱引用:
      也是用来描述一些非必须的对象,但是它是 无论内存够不够都会被会回收
    • 虚引用:
      仅持有虚引用的对象,在任何时候都可能被GC,设置虚引用的唯一目的:就是能在这个对象呗回收时收到一个系统通知。

    注意:即使在可达性分析算法中不可达的对象,也不是 非死不可的,这时候它们暂时处于“缓刑”,真正死亡,至少要经历两次标记的过程:判断对象是否有必要执行finalize()方法;若被判定为有必要执行finalize()方法,之后还会对对象再进行一次筛选,如果对象能在finalize()中重新与引用链上的任何一个对象建立关联,将被移除出“即将回收”的集合。任何对象的 finalize()方法,只会执行一次。

    public class FinalizeEscapeGC {
    
        public static FinalizeEscapeGC SAVE_HOOK = null;
    
        public void isAlive() {
            System.out.println("yes, i am still alive :)");
        }
    
        @Override
        protected void finalize() throws Throwable {
            super.finalize();
            System.out.println("finalize mehtod executed!");
            FinalizeEscapeGC.SAVE_HOOK = this;
        }
    
        public static void main(String[] args) throws Throwable {
            SAVE_HOOK = new FinalizeEscapeGC();
    
            //对象第一次成功拯救自己
            SAVE_HOOK = null;
            System.gc();
            // 因为Finalizer方法优先级很低,暂停0.5秒,以等待它
            Thread.sleep(500);
            if (SAVE_HOOK != null) {
                SAVE_HOOK.isAlive();
            } else {
                System.out.println("no, i am dead :(");
            }
    
            // 下面这段代码与上面的完全相同,但是这次自救却失败了
            SAVE_HOOK = null;
            System.gc();
            // 因为Finalizer方法优先级很低,暂停0.5秒,以等待它
            Thread.sleep(500);
            if (SAVE_HOOK != null) {
                SAVE_HOOK.isAlive();
            } else {
                System.out.println("no, i am dead :(");
            }
        }
    }
    #################
    运行结果:
    finalize mehtod execute!
    yes, i am still alive :)
    no, i am dead :(
    两段代码是一样的,执行结果一次成功,一次失败,这就是上面提到的任何对象的 finalize()方法,只会执行一次。
    

    那么问题来了,知道是垃圾后,要怎么去回收呢?当然就是每个人都会说的垃圾收集算法啦

    • 标记 — 清除算法

    顾名思义,是垃圾 就 标记,然后把标记的给 清除 就好了(标记过程就是上面讲的)
    缺点:效率,空间问题。标记清除后会产生大量的不连续内存碎片。

    为啥会有这样的缺点,不理解?举个例子:

    你种了好多大白菜,一眼看过去,好多个点都有坏死的,而且都不是同一个区域,你依次走过去,标记是要扔掉的,然后就去清理了,这样下来效率就很慢了,而且你拔掉的地方那么小,翻土重新种又太小,容易弄坏其他的白菜,只能空着,那么就是空间问题了。

    复制算法

    把可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用尽后,把还存活着的对象 复制 到另外一块上面,再将这一块内存空间一次清理掉。
    例子:这个就更容易理解了,叫了两打啤酒,每次喝都随机两打里面拿,要结账的时候,把空瓶装一箱,没喝存起来的装一箱,结账时候清理空瓶的那一箱就好了。
    缺点:为了解决效率问题。代价 内存缩小了一半

    标记 — 整理算法

    首先 标记 出所有需要回收的对象,然后进行 整理,使得存活的对象都向一端移动,最后直接清理掉端边界以外的内存。
    例子 :平常我们自己整理房间就是啦,杂乱的房间中 不想要的扔掉,不要让它继续堆放在房间占位置,把空出来的存放自己想要的东西,
    优点:即没有浪费50%的空间,又不存在空间碎片问题,性价比较高。
    一般情况下,老年代会选择标记-整理算法。

    分代收集算法

    根据对象存活周期的不同将内存划分为几块。一般是把 JAVA 堆分为新生代和老年代,然后根据各个年代的特点采用最适当的收集算法。
    新生代:回收时,有大批对象死去,只有少量存活,适合复制算法。
    老年代:存活率高,没有额外的分配担保空间,必须使用 标记 — 清理,或 标记 — 整理。

    相关文章

      网友评论

          本文标题:Java虚拟机(JVM) 之 GC

          本文链接:https://www.haomeiwen.com/subject/gejvwftx.html