直接内存(内存溢出、释放原理) 内存溢出 释放原理

这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战

内存溢出

虽然直接内存(Direct Memory)不受jvm管理,但是它还是会有内存溢出的情况。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
js复制代码    static int ONE_HUNDRED_GB = 1024 * 1024 * 1024;

public static void main(String[] args){
List<ByteBuffer> byteBufferList = new ArrayList<>();
Integer num = 0;
try{
while (true){
// 每次分配的直接内存大小
ByteBuffer bBuf = ByteBuffer.allocateDirect(ONE_HUNDRED_GB);
byteBufferList.add(bBuf);
num++;
// 由于速度太快这里每次让线程休眠一下
Thread.sleep(2000l);
}
}catch (Exception e){
e.printStackTrace();
} finally{
System.out.println(num);
}
}

运行上面这段代码会报出:java.lang.OutOfMemoryError: Direct buffer memory 错误,提示直接内存溢出。

这里运行了三次就报直接内存溢出
image.png

运行前本机内存

image.png

运行时本机内存

image.png

总结

虽然直接内存并不由我们的jvm进行分配管理,但是还是会出现内存溢出的情况。

通过运行前内存和运行时内存可以看出,直接内存并不会占满我们计算机系统的所有内存,当达到一定大小时就会抛出内存溢出信息。

释放原理

直接内存的分配和回收释放主要是用到了一个叫做Unsafe对象。

分配是使用该对象中提供的本地方法 allocateMemory() 方法,在Java程序中最终是通过调用 Unsafe 对象中allocateMemory() 进行直接内存的分配。

回收释放是使用该对象中提供 freeMemory() 的本地方法进行直接内存的回收释放。

主要的体现在 ByteBuffer.allocateDirect 源码中,先点进该源码中。

image.png

再进入 new DirectByteBuffer(capacity) 方法中查看该源码。

image.png

在这里就能看到是通过 unsafe.allocateMemory(size) 方法进行分配的直接内存,然后们再看具体实现?进入会发现这个方法是 native 的,该方法的实现由其它的语言进行实现的没法查看,但是我们知道直接内存最终是通过该方法分配的就行。

我们继续找直接内存的释放,这段代码不能直接看到释放的方法,我们进入 new Deallocator(base, size, cap) 这里面,然后会发现下面有个run() 方法,该方法中调用了直接内存释放的方法 unsafe.freeMemory(address);

image.png

那么到底是怎么执行该 run() 方法的,看前面那张图主要是通过 cleaner(虚引用类型)它的特点是:当它所关联的这个对象被jvm回收时就会触发 Cleaner.clean 方法。

这里它关联的是ByteBuffer 对象,该对象是java对象,当该对象被垃圾回收掉时它就会执行 Cleaner 对象中 clean 方法。

查看 Cleaner 对象中clean方法

image.png

在这里会调用到刚才的 run() 方法进行直接内存的回收释放。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%