系统相关
首页 > 系统相关> > jvm学习(一)DirectByteBuffer堆外内存浅析

jvm学习(一)DirectByteBuffer堆外内存浅析

作者:互联网

问题

堆内外内存

java 进程的内存占用到底是怎么样的呢?

1297993-20200707181329640-409268722.png

我们都知道 jvm 有垃圾回收机制,并且回收的重点区域就是堆,假如我们以堆内堆外来区分内存区域,上图所示

DirectByteBuffer 类

以下描述代码图片来自 : https://blog.csdn.net/mycs2012/java/article/details/93513057 , 非原创 

DirectByteBuffer 使用

public static void main(String[] args) throws Exception {

	// 分配
	ByteBuffer buffer = ByteBuffer.allocateDirect(128);

	// 写入
	buffer.put("写入到直接内存".getBytes(Charset.forName("utf-8")));

	// 读取
	buffer.flip();
	byte[] bytes = new byte[buffer.remaining()];
	buffer.get(bytes);
	System.out.println(new String(bytes, Charset.forName("utf-8")));

	System.gc();	// 不是必须
}

内部原理

下面是 DirectByteBuffer 的构造方法

DirectByteBuffer(int cap) {  

    super(-1, 0, cap, cap);
    boolean pa = VM.isDirectMemoryPageAligned();	// 获取是否开启内存页对齐选项
    int ps = Bits.pageSize();		// 内存页大小
    long size = Math.max(1L, (long)cap + (pa ? ps : 0));	// 计算size,后面按size进行实际内存占用
    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;
}

主要的操作有 : 1、通过unsafe.allocateMemory(size)分配一段大小为size的内存,这是个native方法,表明会通过JNI调用操作系统本地的系统调用接口。该方法最终会调用操作系统的malloc方法,进行内存的分配,分配成功后返回一个基地址,这个基地址最后转换为address,DirectByteBuffer对象就是通过address和size引用这段内存。

2、创建Cleaner对象,后续用于清理直接内存。

而这个 Cleaner 类是如何达到回收内存的效果的呢 , Cleaner 对象会持有Deallocator,在执行收集的时候调用其 run 方法

public class Cleaner extends PhantomReference<Object> { 
    ....
}

Cleaner 是虚引用的之类,虚引用的容易被回收,当被回收就回调用 Cleaner 的 clean 方法

private static class Deallocator
    implements Runnable
    {
 
        private static Unsafe unsafe = Unsafe.getUnsafe();
 
        private long address;
        private long size;
        private int capacity;
 
        private Deallocator(long address, long size, int capacity) {
            assert (address != 0);
            this.address = address;
            this.size = size;
            this.capacity = capacity;
        }
 
        public void run() {
            if (address == 0) {
                // Paranoia
                return;
            }
            // 使用unsafe方法释放内存
            unsafe.freeMemory(address);
            address = 0;
            // 更新统计变量
            Bits.unreserveMemory(size, capacity);
        }
 
    }

参考

标签:Cleaner,堆外,base,内存,jvm,address,DirectByteBuffer,浅析,size
来源: https://www.cnblogs.com/Benjious/p/13274438.html