netty的对象的生命周期管理——引用计数

来源:互联网 发布:串口调试助手软件 编辑:程序博客网 时间:2024/05/17 22:11

一、何谓引用计数

1.1 引用计数

引用计数是一种简单高效的垃圾回收机制,当一个新的引用指向对象的时候,增加引用计数;去掉一个引用,就减小一个引用计数;当引用计数减到0的时候,就释放掉这个引用计数。


1.2 引用计数的增减

   对于一个引用计数的对象,最核心的操作就是引用计数的增减。但是,在一个并发场景下,必须保证引用计数增减的原子性。最简单的实现方法如下:

private static class AtomicIntSync implements Atomic {        private int i = 0;        public AtomicIntSync(int i) {            this.i = i;        }        @Override        public synchronized int incrementAndGet() {            return ++i;        }    }

这种方法通过synchonized关键字,为自增操作加锁,而且java对这个关键字做了很多的优化,相对而言性能还是挺高的。


稍微复杂一点的方式是使用CAS(Compare and swap)的方式,CAS的语义是“我认为V的值应该为A,如果是,那么将V的值更新为B,否则不修改并告诉V的值实际为多少”,实现如下:

private static class AtomicIntCas implements Atomic {        private volatile int        i = 0;        private static final Unsafe unsafe;        private static long         off;        static {            Unsafe tmp = null;            try {                //获取unsafe                Field field = Unsafe.class.getDeclaredField("theUnsafe");                field.setAccessible(true);                tmp = (Unsafe) field.get(null);                //获取i字段在类中的偏移                off = tmp.fieldOffset(AtomicIntCas.class.getDeclaredField("i"));            } catch (Throwable e) {                e.printStackTrace();            }            unsafe = tmp;        }        public AtomicIntCas(int i) {            this.i = i;        }        @Override        public int incrementAndGet() {            for (;;) {                int m = i;                int add = m + 1;                if (unsafe.compareAndSwapInt(this, off, m, add)) {                    return add;                }            }        }    }
整个性能对比的程序如附件所示,运行后的性能对比曲线如下,(x轴为线程的数量,y轴为每一个线程运行的时间):


由上图可知,CAS方式整体性能是好于使用synchronized的这种锁的方式的。


二、引用计数的应用

     引用计数的应用非常多,大名鼎鼎的Python语言其简单对象的垃圾回收机制就用的引用计数,netty的大量数据结构也使用的是引用计数。因为引用计数的垃圾回收实现简单,而且非常高效,特别是在分布式环境中,其性能真心杠杠的。


三、引用计数的局限

    引用计数很难解决的场景是:A引用B,B同时引用A,当A和B都不使用了,A和B的引用都会是1,导致这种场景下的引用计数无法变成0,导致A和B都不能被释放。

四、netty的引用计数的实现

    netty的引用计数的接口为io.netty.util.ReferenceCounted,netty很多重要的数据结构都是继承这个接口,比如ByteBuf,其核心实现参考:io.netty.util.AbstractReferenceCounted,其实现方式和上面的CAS的实现方法原理基本一致。

过程是:

1、通过Unsafe的方法,计算引用计数字段在AbstractReferenceCounted类中的偏移

2、通过Unsafe提供的CAS方法,将新的数据搬迁到引用计数字段所在的位置(AbstractReferenceCounted对象的地址+偏移)

3、如果CAS操作成功,就返回;否则计算新的引用计数,然后调用步骤2


五、netty的内存泄露检查

详见下一篇文章~

六、netty的引用计数的使用举例

0 0
原创粉丝点击