JVM-直接内存原理

本文最后更新于:2022年1月13日 下午

JVM直接内存

1. 直接内存原理

直接内存是操作系统内存的一部分,并不是由虚拟机管理的内存,不受垃圾回收管理器的管理。

​ 正常情况下的内存使用:

1

根据上图所示,java缓冲区的内存是由JVM进行分配和回收的,JVM本身相对于操作系统来说属于用户态的应用程序,如果进行文件读写操作,需要由用户态转到内核态,由操作系统提供读写服务。因此在进行文件读写时,需要进过两次拷贝。

​ 直接内存的使用情况:

2

根据上图可见,当java程序申请直接内存时,使用的的系统内存,并且java程序和操作系统都可以对这块内存进行操作,因此可以加快读写速度,但是直接内存不受GC的管理,分配的代价也比较大,常用于NIO操作,用作读写缓冲区。

当然,如果申请的直接内存过大,会出现OutOfMemoryError异常。

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

​ 使用ByteBuffer来申请直接内存:

1
ByteBuffer  buffer = ByteBuffer.allocateDirect(1024);
1
2
3
public static ByteBuffer allocateDirect(int capacity) {
return new DirectByteBuffer(capacity);
}

DirectByteBuffer的内部实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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;
}

根据源码,我们可以看到申请内存实际上是通过一个UNSAFE对象来实现的,base = UNSAFE.allocateMemory(size);,通过使用allocateMemory方法来申请直接内存。同样在释放直接内存时,需要我们主动调用UNSAFE的freeMemory方法来释放内存。

具体在DirectByteBuffer的实现方法中,我们看到了使用了一个Cleaner(虚引用)来检测this,就是我们当前的ByteBuffer对象,一旦ByteBuffer对象被垃圾回收,那么就会由ReferenceHandler线程通过Cleaner的clean方法来调用freeMemory方法来释放直接内存。

根据源码可以看到,在创建Cleaner的同时,传入了Deallocator对象,在Deallocator对象存在一个run方法,该run方法内部调用了UNSAFE的freeMemory方法来释放直接内存。

1
2
3
4
5
6
7
8
9
 public void run() {
if (address == 0) {
// Paranoia
return;
}
UNSAFE.freeMemory(address);
address = 0;
Bits.unreserveMemory(size, capacity);
}

在实际使用中,当ByteBuffer对象被回收后,Cleaner便可以通过UNSAFE的freeMemory方法来回收直接内存,当然,我们也可以直接调用UNSAFE的freeMemory方法来回收直接内存,但是UNSAFE一般由JVM内部使用,上层程序无法直接使用,需要通过反射来间接获取UNSAFE对象。


本文作者: ziyikee
本文链接: https://ziyikee.fun/2022/01/08/JVM-%E7%9B%B4%E6%8E%A5%E5%86%85%E5%AD%98%E5%8E%9F%E7%90%86/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!