栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

【大白话系列】深入浅出Cleaner+虚引用完成堆外内存的回收

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【大白话系列】深入浅出Cleaner+虚引用完成堆外内存的回收

在NIO技术中,使用allocateDirect()方法可以创建直接内存;如何释放该内存呢?

(1)通过手动释放内存(Cleaner+虚引用)
(2)交给JVM进行处理(Full GC)

文章目录
  • 1.直接内存的创建与销毁
  • 2.通过Cleaner+虚引用完成堆外内存回收
  • 3.总结创建与销毁流程
  • 4.如何一步步顺序解读源码流程
  • 5.使用直接内存的利弊分析

1.直接内存的创建与销毁
base = UNSAFE.allocateMemory(size);

在DirectByteBuffer类构造方法中,主要通过allocateMemory方法完成堆外内存的创建,这个方法是Unsafe类中,这个类是干什么的呢?通过名字我们可以也可以看出这个类是不安全的,也就是它是能够直接操作堆外内存,超出了JVM的管辖范围!

UNSAFE.freeMemory(address);

需要注意的是通过Unsafe开辟的直接内存,需要通过调用freeMemory手动回收(当然帮你写了)

2.通过Cleaner+虚引用完成堆外内存回收

Java对象有四种引用方式:强软弱虚
虚引用PhantomReference一般来说极少使用,而且它不能单独使用,它需要和引用队列 ReferenceQueue一块使用

首先分析allocateDirect()方法底层(DirectByteBuffer类)

通过它的构造方法可以看出创建了Cleaner对象和Deallocator对象,首先分析Cleaner对象,通过看它的源码可以得出,它的底层维护了一个双向链表,当Cleaner对象初始化时,就会加入到这个Cleaner双向链表中(而且是安全的,使用了Synchronized,Cleaner类继承了虚引用)

   public static Cleaner create(Object ob, Runnable thunk) {
        if (thunk == null)
            return null;
        return add(new Cleaner(ob, thunk));
    }

当DirectByteBuffer对象不存在时,Cleaner对象不再处于引用链中,等到下一次GC时,Cleaner会被加入到ReferenceQueue队列中,并执行clean方法;将将自身从Cleaner双向链表中移除(remove),然利用多态调用了Deallocator类中的run方法释放内存

public void clean() {
        if (!remove(this))
            return;
        try {
            thunk.run();
        } catch (final Throwable x) {
            AccessController.doPrivileged(new PrivilegedAction<>() {
                    public Void run() {
                        if (System.err != null)
                            new Error("Cleaner terminated abnormally", x)
                                .printStackTrace();
                        System.exit(1);
                        return null;
                    }});
        }
    }
public void run() {
            if (address == 0) {
                // Paranoia
                return;
            }
            UNSAFE.freeMemory(address);
            address = 0;
            Bits.unreserveMemory(size, capacity);
        }
3.总结创建与销毁流程

分析下图总结流程(来源百度图片)

初始时,创建了一个DirectByteBuffer对象,在DirectByteBuffer的构造函数中创建了一个Cleaner对象和Deallocator对象;Cleaner对象初始化时将自身加入到了链表中;一旦DirectByteBuffer对象被回收,那么下次GC时Cleaner(继承了虚引用)对象会被放到ReferenceQueue队列中并执行clean方法(多态调用了Deallocator中的run方法释放内存)



4.如何一步步顺序解读源码流程

(1)ByteBuffer抽象类的allocateDirect方法

(2)DirectByteBfufer的构造方法
(3)Cleaner类的create方法
(4)Cleaner类的add方法
(5)回到第二步

cleaner = Cleaner.create(this, new Deallocator(base, size, cap));

(6)Deallocator类的构造方法

将Cleaner类中分配的直接内存的地址、大小等进行复制,因为这个类的run方法是进行内存回收的根方法。

5.使用直接内存的利弊分析

使用直接内存肯定也有坏处,否者还有堆内存什么事;使用直接内存不受JVM的管辖,稍有不慎容易造成内存泄漏!当然使用直接内存减少了GC的时间(交给了操作系统进行管理),另一方面与磁盘交互时使用直接内存减少了复制操作,效率得到提高。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/581710.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号