深入了解Javax虚拟机_JVM高级特性与最佳实践读书笔记(二)

<<深入了解Javax虚拟机_JVM高级特性与最佳实践>>读书笔记(二)

判断对象是否需要被回收

  • 引用计数算法

    给每一个对象添加一个引用计数器,每当有一个地方引用时,计数器就加1 ,当引用失效的时候,计数器减1

    **缺点:**无法解决对象之间相互循环引用的问题。

    public class ReferenceCountGC {
    
        public Object instance = null;
    
        public static void main(String[] args) {
            ReferenceCountGC ob1 = new ReferenceCountGC();
            ReferenceCountGC ob2 = new ReferenceCountGC();
            ob1.instance = ob2;
            ob2.instance = ob1;
            ob1 = null;
            ob2 = null;
    
            System.gc();
    
        }
    

    上述代码,对象ob1和ob2都有字段instance,赋值令ob1.instance = ob2ob2.instance = ob1,除此之外,没有其他引用,但实际上这2个对象已经不可能再被访问了,但他们因为互相引用这对方,他们的引用计数器都不是0,这也导致如果用引用计数算法的话,这2个对象将不被回收。

  • 可达性分析算法

    以“GC Roots”为起始点,从这些节点开始向下搜素,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则可以证明这个对象不可用,就是可以被回收。
    在这里插入图片描述

    如图,object4、object5、object6 3个对象虽然互相有联系,但他们到GC Root是不可达,所以这3个对象是可以被回收。

    可以作为GC Roots对象:

    • 虚拟机栈(栈帧中的本地变量表)中的引用的对象
    • 方法区中的类静态属性引用对象
    • 方法区中常量引用的对象
    • 本地方法栈中JNI(一般指Native方法)引用的对象
finalize方法

当经过上述可达性分析算法被判为不可达的对象时,不一定会被回收,只是处于带回收状态。一个对象要被回收,要经历2次标记,当用可达性分析算法算出改对象到GC Roots不可达的时候,被标记一下,并且判断该对象是否有必要运行finalize()方法(如果该对象没有重载finalize方法,或者已经执行过一次,都会被视为“没有必要执行”)。

如果被判断为有必要这行,那这个对象将会被放置到一个F-queue队列中,由虚拟机自动建立一个低优先级的Finalize线程来触发这个方法。然后GC会对F-queue中的对象进行二次标记,如果finalize方法中让对象与引用链上的一个对象建立关联,那么第二次标记就会被移出队列中,如果没有建立关联,就会被GC回收。

package com.coder.qiang.jdk;

/**
 * @author gengqiang
 * @date 2018/11/26
 */
public class FinalizeEscapeGc {

    public static FinalizeEscapeGc SAVE_HOOK = null;


    private void isAlive() {
        System.out.println("yes, I'm staill alive");
    }


    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalize method executed");
        FinalizeEscapeGc.SAVE_HOOK = this;
    }


    public static void main(String[] args) throws InterruptedException {
        SAVE_HOOK = new FinalizeEscapeGc();
        SAVE_HOOK = null;
        System.gc();
        Thread.sleep(500);

        if (SAVE_HOOK!=null )
        {
            SAVE_HOOK.isAlive();
        }else{
            System.out.println("sorry! I'm dead!");
        }


        SAVE_HOOK = null;
        System.gc();
        Thread.sleep(500);

        if (SAVE_HOOK!=null )
        {
            SAVE_HOOK.isAlive();
        }else{
            System.out.println("sorry! I'm dead!");
        }

    }
}


output:
finalize method executed
yes, I'm staill alive
sorry! I'm dead!

上述代码就验证了finalize方法执行后,没有被GC回收,但第二次,就被GC回收了。

垃圾收集算法

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

    和名字一样,这个算法有2个阶段 标记,清除

    • 标记

      标记处所有需要清除的对象

    • 清除

      统一回收所有被标记的对象

    缺点:

    • 效率问题,标记,清除的效果都不高
    • 空间问题,标记清除后回造成大量不连续的空间碎片,这就可能导致后续如果需要分配较大内存的时候,因为没有找到足够的连续内存回不得提前再次触发垃圾回收操作。

    在这里插入图片描述

    ​ 上图就是标记 - 清除算法的大概过程。

  • 复制算法

    将内存按容量划分为大小相等的两块,每次都使用一块,当一块用完,就将还存活的对象复制到另外一块,然后把已使用的内存空间一次性清理掉。这样每次都是对整个版区的内存回收,不用考虑内存碎片的问题了。

    **缺点:**代价太大,需要讲内存缩小到一半。

在这里插入图片描述

​ 上图就是复制算法的大概过程。

  • 标记 - 整理算法

    和名字一样,这个算法有2个阶段 标记,清除

    • 标记

      标记处所有需要清除的对象

    • 整理

      存活的对象往一端移动,然欧清理掉端边界以外的内存。

      在这里插入图片描述

  • 分代收集算法

把内存分为不同区域,不同的区域的垃圾收集算法不一样。例如Java堆中分为新生代和老年代,新生代每次垃圾收集都会有大量对象被回收,所以一般都会用复制算法。而老年代因为存活率高,所以用标记 -清理 或者标记-整理算法来进行回收。

垃圾收集器

如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。

HotSpot虚拟机上的垃圾收集器:

  • Serial收集器
  • ParNew收集器
  • Parallel Scavenge收集器
  • Serial old收集器
  • Parallel old收集器
  • CMS收集器
  • G1收集器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Coder_Qiang

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

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

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

打赏作者

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

抵扣说明:

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

余额充值