netty Recycler(一)

24 篇文章 1 订阅
11 篇文章 0 订阅

此文章是研究netty过程中的记录,很有可能有很多不对的地方,欢迎指正

之所以研究netty Recycler,是因为生产环境中遇到了由其引发的堆内存占用率过高的现象
实验所用源码

回收对象分为两种:同一线程 不同线程。

同一线程

在同一线程中,对象回收至Recycler$Stack中

实验代码

package test.recycler;

import io.netty.util.Recycler;
import io.netty.util.concurrent.FastThreadLocalThread;

import java.rmi.server.ExportException;
import java.util.ArrayList;
import java.util.List;

/**
 * unreachable Recycler$DefaultHandle
 */
public class RecycleNoRelease1 {
    private static final Recycler<User> RECYCLER = new Recycler<User>() {
        //没有对象的时候,新建一个对象, 会传入一个handler,在Recycler池里,所有的对象都会转成DefaultHandle对象
        @Override
        protected User newObject(Handle<User> handle) {
            return new User(handle);
        }
    };
    private static ThreadLocal<Long> numLong = new ThreadLocal<Long>();

    private static class User {
        private final Recycler.Handle<User> handle;
        public long a1 = 1;//1kb
        public long a2 = 2;
        public long a3 = 1;//1kb
        public long a4 = 2;

        public long a5 = 1;//1kb
        public long a6 = 2;
        public long a7 = 1;//1kb
        public long a8 = 2;


        public User(Recycler.Handle<User> handle) {
            this.handle = handle;
        }

        public void recycle() {
            //通过handler进行对象的回收
            handle.recycle(this);
        }
    }

    public static void main(String[] args) throws InterruptedException {
//        try {
//            //此处添加延时是为了让我来得及打开JVisualVM去查看动态变化并使用jmap生成dump文件
//            Thread.sleep(20000);
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
        System.out.println("begin");
        FastThreadLocalThread thread = new FastThreadLocalThread(new Runnable() {
            public void run() {
                long nnn = 1024 * 32;
                List<User> list1 = new ArrayList<User>();
                for (int i = 0; i < nnn; ++i) {
                    list1.add(RECYCLER.get());
                }
                for (int i = 0; i < nnn; ++i) {
                    list1.get(i).recycle();
                }
            }
        });

        thread.start();
        thread.join();
        System.out.println("=======================over===================================");

        while (true) {
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

通过debug发现,回收对象时会将对象存储至stack中
在这里插入图片描述
在这里插入图片描述
上面两张图说明线程的ThreadLocal里存储了592这个对象,而本次会回收的对象就是592
在这里插入图片描述

并不是每一次调用回收方法的时候对象都能回收到stack中,回收的过程中有一个判断

boolean dropHandle(DefaultHandle<?> handle) {
            if (!handle.hasBeenRecycled) {
                if (handleRecycleCount < interval) {
                    handleRecycleCount++;
                    // Drop the object.
                    return true;
                }
                handleRecycleCount = 0;
                handle.hasBeenRecycled = true;
            }
            return false;
        }

如果上面的结果为true,对象必然不会进入stack中。如果为false,上层函数还会进行其他判断。
interval是间隔的意思。当运行RecycleInterval(github中的代码)时可以看到对象周期性的入stack

当线程结束时,592这种对象就会变成unreachable
在这里插入图片描述

线上曾经遇到DefaultHandle直接为unreachable,推测可能是jvm将DefaultHandle上一层对象(此例中为RecycleNoRelease1$User)回收了。但是jvm会只回收上一层对象而不回收DefaultHandle吗?这部分还需要做实验。

不同线程

实验代码

package test.recycler;

import io.netty.util.Recycler;
import io.netty.util.concurrent.FastThreadLocalThread;

import java.util.ArrayList;
import java.util.List;

/**
 * thread用于创建对象,thread2用于回收对象。用于引发WeakOrderQueue$Link的unreachable
 */
public class RecycleMultiThread {
    private static final Recycler<User> RECYCLER = new Recycler<User>() {
        //没有对象的时候,新建一个对象, 会传入一个handler,在Recycler池里,所有的对象都会转成DefaultHandle对象
        @Override
        protected User newObject(Handle<User> handle) {
            return new User(handle);
        }
    };
    private static Object lock = new Object();

    private static class User {
        private final Recycler.Handle<User> handle;
        public long a1 = 1;//1kb
        public long a2 = 2;
        public long a3 = 1;//1kb
        public long a4 = 2;

        public long a5 = 1;//1kb
        public long a6 = 2;
        public long a7 = 1;//1kb
        public long a8 = 2;


        public User(Recycler.Handle<User> handle) {
            this.handle = handle;
        }

        public void recycle() {
            //通过handler进行对象的回收
            handle.recycle(this);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        try {
            //此处添加延时是为了让我来得及打开JVisualVM去查看动态变化并使用jmap生成dump文件
            Thread.sleep(20000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("begin");
        final List<User> list1 = new ArrayList<User>();
        FastThreadLocalThread thread = new FastThreadLocalThread(new Runnable() {
            public void run() {
                long nnn = 1024 * 32;
                for (int i = 0; i < nnn; ++i) {
                    list1.add(RECYCLER.get());
                }

                try {
                    System.out.println("thread lock begin");
                    synchronized (RecycleMultiThread.class) {
                        RecycleMultiThread.class.notify();//对象创建完毕
                        RecycleMultiThread.class.wait();
                    }
                    System.out.println("thread lock over");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        FastThreadLocalThread thread2 = new FastThreadLocalThread(new Runnable() {
            public void run() {
                long nnn = 1024 * 32;
                synchronized (RecycleMultiThread.class) {
                    try {
                        RecycleMultiThread.class.wait();//等待对象创建完毕
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                for (int i = 0; i < 1024 * 32; ++i) {
                    list1.get(i).recycle();
                }
                try {
                    System.out.println("thread2 notify");
                    synchronized (RecycleMultiThread.class) {
                        RecycleMultiThread.class.notify();
                    }
                    System.out.println("thread2 notify over");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

        thread.start();
        thread2.start();

        thread.join();
        thread2.join();
        System.out.println("=======================over===================================");

        while (true) {
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

回收至WeakOrderQueue中
在这里插入图片描述
在这里插入图片描述
查找上图中@0xffd8aa30的最短路径
在这里插入图片描述
为unreachable

此现象与线上系统的dump文件相似(线上dump文件直接就是WeakOrderQueue L i n k u n r e a c h a b l e ) , 据 此 推 测 线 上 老 年 代 出 现 大 量 不 可 达 对 象 的 原 因 就 在 于 线 程 退 出 时 其 T h r e a d L o c a l 依 旧 在 老 年 代 占 有 大 量 空 间 , 并 且 由 于 线 程 已 经 退 出 , 所 以 W e a k O r d e r Q u e u e Link unreachable),据此推测线上老年代出现大量不可达对象的原因就在于线程退出时其ThreadLocal依旧在老年代占有大量空间,并且由于线程已经退出,所以WeakOrderQueue Linkunreachable线线退ThreadLocal线退WeakOrderQueueLink直接变成unreachable

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Netty 是一个基于 NIO 的客户端、服务器端编程框架,使用 Java 语言编写。它提供了一种高效、可靠、可扩展的异步事件驱动网络编程模型,可以简化网络编程的开发流程。 下面是 Netty 的源码剖析: 1. Bootstrap 类:这是 Netty 启动类,它提供了启动客户端和服务器的方法。其中,ServerBootstrap 类用于启动服务器端应用,Bootstrap 类用于启动客户端应用。 2. Channel 类:这是 Netty 中最核心的类,表示一个通道,可以用来进行数据的读写操作。它继承了 Java NIO 中的 Channel 接口,并添加了一些新的方法和属性,如ChannelPipeline、ChannelHandlerContext 等。 3. ChannelPipeline 类:这是 Netty 中的另一个核心类,表示一组 ChannelHandler 的有序列表,用于管理数据的处理流程。在 Netty 中,一个 Channel 对象可以有多个 ChannelPipeline 对象,每个 ChannelPipeline 对象包含多个 ChannelHandler 对象。 4. ChannelHandlerContext 类:这是 Netty 中的上下文对象,表示一个 ChannelHandler 对象和它所在的 ChannelPipeline 对象之间的关联关系。它提供了一些方法,可以访问 ChannelPipeline 中的其他 ChannelHandler 对象。 5. ChannelFuture 类:这是 Netty 中的异步操作结果对象,表示一个异步操作的状态和结果。当一个异步操作完成时,会通知关联的 ChannelFuture 对象,从而使应用程序能够得到异步操作的结果。 6. EventLoop 类:这是 Netty 中的事件循环对象,用于处理所有的 I/O 事件和任务。在 Netty 中,一个 EventLoop 对象会被多个 Channel 对象共享,它负责调度和执行所有与这些 Channel 相关的事件和任务。 7. ByteBuf 类:这是 Netty 中的字节缓冲区对象,用于存储和操作字节数据。与 Java NIO 中的 ByteBuffer 对象相比,ByteBuf 提供了更加灵活和高效的读写方式。 8. ChannelHandler 接口:这是 Netty 中的处理器接口,用于处理读写事件和状态变化事件。它提供了多个方法,如 channelActive、channelRead、channelWrite 等,用于处理不同类型的事件。 以上是 Netty 的源码剖析,了解这些核心类和接口可以更好地理解和使用 Netty 框架。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值