netty内存泄露检测原理

一、为什么netty需要内存泄露检测

由于netty的ByteBuf可能申请自直接内存,这一块是内存是不纳入GC的,如果不释放,会导致直接内存泄露。


二、虚引用

虚引用在实际的引用被释放之前,会将虚引用保存到引用队列中,

ReferenceQueue referenceQueue = new ReferenceQueue();

        Object oc = new Object();
        PhantomReference data = new PhantomReference(oc, referenceQueue);
        oc = null;
        System.gc();
        System.out.println("队列中的引用:" + referenceQueue.poll());
        System.out.println("虚引用中的对象:" + data.get()); 
  
运行结果:

队列中的引用:java.lang.ref.PhantomReference@70a31398

虚引用中的对象:null

利用虚引用的这个特点,可以进行内存泄露检测,检测原理是:

a、继承PhantomReference,在其中记录实际的被引用对象的状态,是否进行了释放等等;

b、在被引用对象被gc回收时,检测被引用对象,是否进行了释放,如果没有释放,打印内存泄露信息

详细可进一步参考这篇文章:http://blog.sina.com.cn/s/blog_4a4f9fb50100u908.html


三、实现原理简介

a、继承PhantomReference

/**
     * 用于内存泄露检测的数据
     * 
     * @author fengjing.yfj
     * @version $Id: DetectorTest.java, v 0.1 2015年10月11日 下午4:21:47 fengjing.yfj Exp $
     */
    private static class DetetorData extends PhantomReference {

        /** 是否已经释放掉 */
        private AtomicBoolean freed;

        public DetetorData(Object referent, ReferenceQueue q) {
            super(referent, q);
            freed = new AtomicBoolean(false);
        }

        /**
         * 如果还没有释放,则设置freezed=true,并且返回true;否则直接返回false
         * 
         * @return
         */
        public boolean close() {
            if (freed.compareAndSet(false, true)) {
                return true;
            }
            return false;
        }

    }被引用对象在引用计数减为0,进行资源释放的,需要调用 
  DetetorData.close()方法,更改资源状态;如果引用队列中的引用计数对象没有调用过这个方法,说明引用计数没有被正确的释放。 
  

b、实现检测工具

/**
     * 检测工具
     * 
     * @author fengjing.yfj
     * @version $Id: DetectorTest.java, v 0.1 2015年10月11日 下午4:34:40 fengjing.yfj Exp $
     */
    public static class Detector {

        /** 虚引用的保存的队列,虚引用被GC前,会保存到该队列中 */
        private ReferenceQueue referenceQueue = new ReferenceQueue();

        public DetetorData open(T object) {
            //内存泄露检测,只能检测上一个对象是否发生了内存泄露
            leakDetect();
            //为新的对象创建虚引用
            return new DetetorData(object, referenceQueue);
        }

        private void leakDetect() {
            for (;;) {
                DetetorData data = (DetetorData) referenceQueue.poll();

                if (data == null) {
                    break;
                }

                //close成功,说明DetetorData没有关闭,也即引用计数没有到0
                if (data.close()) {
                    System.out.println("内存泄露......");
                }
            }
        }
    }c、自定义引用计数 
  


private static class MyRefCount extends AbstractReferenceCounted {

        /** 检测的数据 */
        private DetetorData detetorData;

        public MyRefCount(Detector detector) {
            detetorData = detector.open(this);

        }

        @Override
        public ReferenceCounted touch(Object hint) {
            return this;
        }

        public DetetorData dump() {
            System.out.println("This=" + this + " " + detetorData);
            return detetorData;
        }

        /**
         * 当引用计数变成0的时候,会调用内存泄露检测数据的关闭
         * @see io.netty.util.AbstractReferenceCounted#deallocate()
         */
        @Override
        protected void deallocate() {
            detetorData.close();
        }

    }

四、netty的实现

netty的内存泄露检测原理和三中的是一样的,只是netty的内存泄露检测加入的检测级别的概念,

public enum Level {
        /**
         * Disables resource leak detection.
         */
        //取消内存泄露检测
        DISABLED,
        /**
         * Enables simplistic sampling resource leak detection which reports there is a leak or not,
         * at the cost of small overhead (default).
         */
        //使用抽样检测的方式(抽样间隔为:samplingInterval),并且仅仅只是打印是否发生了内存泄露,
        SIMPLE,
        /**
         * Enables advanced sampling resource leak detection which reports where the leaked object was accessed
         * recently at the cost of high overhead.
         */
        ///使用抽样检测的方式(抽样间隔为:samplingInterval),并且打印哪里发生了内存泄露
        ADVANCED,
        /**
         * Enables paranoid resource leak detection which reports where the leaked object was accessed recently,
         * at the cost of the highest possible overhead (for testing purposes only).
         */
        //对每一个对象都进行检测,并且打印内存泄露的地方,我就是这么任性,这么偏执。负载较高,适合测试模式
        PARANOID
    }


对于不同的级别,会有不同的处理。

netty的实现详见:io.netty.util.ResourceLeakDetector和io.netty.util.ResourceLeakDetector.DefaultResourceLeak类,只要理解了三中的原理,应该可以很好理解源代码的,这里就不在冗述~



你可能感兴趣的:(netty内存泄露检测原理)