直接内存 直接内存的释放和回收

直接内存

特点

  • 不属于Java虚拟机管理,属于系统内存;
  • 属于操作系统,常见于NIO操作时,比如ByteBuffer【】用于数据缓冲区
  • 分配回收成本较高,但读写性能高;
  • 不受JVM内存回收管理

文件读写过程

java不具备磁盘读写的能力,因此需要调用操作系统提供的方法;java函数->本地方法用户态->内核态

弊端:需要在系统内存和java堆内存都复制一份磁盘文件,浪费空间也降低传输效率

在这里插入图片描述

直接内存是在系统内存和Java堆内存之间开辟出的一块共享区域,可以供操作系统和java代码访问。

好处:只需要缓冲一份磁盘文件,效率更高

在这里插入图片描述

内存溢出

​ 直接内存也会存在内存溢出的问题,比如下面的例子,在循环中不断申请一个100m的直接内存空间,把其加入list中,最终会造成直接内存不够,抛出直接内存溢出异常

在这里插入图片描述

可以在任务管理中查看到当前直接内存的大小

在这里插入图片描述

直接内存释放原理

java底层使用Unsafe类来分配释放直接内存,一般是jdk内部使用

在这里插入图片描述

//通过ByteBuffer申请100M的直接内存
//容量默认单位为字节Byte
int _100M = 1024*1024;   
ByteBuffer byteBuffer=ByteBuffer.allocateDirect(_100M);

allocateDirect实现过程

/**
 * Allocates a new direct byte buffer.
 * @param  capacity
     *         The new buffer's capacity, in bytes
 */
public static ByteBuffer allocateDirect(int capacity) {   
    return new DirectByteBuffer(capacity);
}

DirectByteBuffer实现过程

  • 调用 UNSAFE.setMemory(base, size, (byte) 0);来分配直接内存
  • cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
  • 调用 UNSAFE.freeMemory(address);来释放直接内存
// Primary constructor
//
DirectByteBuffer(int cap) {                   // package-private

    super(-1, 0, cap, cap, null);
    boolean pa = VM.isDirectMemoryPageAligned();
    int ps = Bits.pageSize();
    long size = Math.max(1L, (long)cap + (pa ? ps : 0));
    Bits.reserveMemory(size, cap);

    long base = 0;
    try {
        base = UNSAFE.allocateMemory(size);
    } catch (OutOfMemoryError x) {
        Bits.unreserveMemory(size, cap);
        throw x;
    }
    UNSAFE.setMemory(base, size, (byte) 0);
    if (pa && (base % ps != 0)) {
        // Round up to page boundary
        address = base + ps - (base & (ps - 1));
    } else {
        address = base;
    }
    cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
    att = null;

}

Deallocator的实现过程

public void run() {
    if (address == 0) {
        // Paranoia
        return;
    }
    UNSAFE.freeMemory(address);
    address = 0;
    Bits.unreserveMemory(size, capacity);
}

cleaner是虚引用类型,当被关联的对象(ByteBuffer)被java垃圾回收时,会触发cleaner的clean方法,执行 Deallocator任务对象的run方法,即最终调用 UNSAFE.freeMemory(address);

在这里插入图片描述

禁用显示回收堆直接内存的影响

代码中的System.gc是****显示垃圾回收,是一次Full GC 比较耗时,影响程序执行;可以使用命令**禁用显示回收,**但是会造成ByteBuffer对象存活,直接内存无法释放,造成长时间得不到释放的问题。

解决方法:直接使用Unsafe调用FreeMemory手动释放 UNSAFE.freeMemory(address);

可以使用命令**禁用显示回收,**但是会造成ByteBuffer对象存活,直接内存无法释放,造成长时间得不到释放的问题。

解决方法:直接使用Unsafe调用FreeMemory手动释放 UNSAFE.freeMemory(address);

在这里插入图片描述

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值