堆外内存DirectByteBuffer

堆外内存: 不进行GC,防止JNI访问错位。

使用方式

使用堆外内存的两种方式:
1.隐式:比如读写文件时:

读文件: 文件(disk)=>堆外内存=>堆内内存;(过程中JVM保证不GC)
写文件: 堆内内存=>堆外内存=>文件(disk)。

2.显式:使用DirectByteBuffer,直接在堆外分配空间,节省1倍空间,减去一倍拷贝操作。

1
2
// 底层源码:
unsafe.allocateMemory(size)

Unsafe

java直接管理内存用的类,之所以叫Unsafe,意思是这些原来设计是给sun公司的人专用的,不是开放给外面的人用的,希望普通用户不要依赖它的接口,随时随着jdk版本升级而更改接口。// 如果不升级jdk,就不用care了。
并不是说这个类不安全。

使用场景

(因为不gc)
生命期较长的大对象;
创建次数不会太多。
// 如直接的文件拷贝操作。

优点

1.对于大内存有良好的伸缩性
2.对垃圾回收停顿的改善可以明显感觉到
3.在进程间可以共享,减少虚拟机间的复制

配置

1
2
3
-XX:MaxDirectMemorySize
-Dsun.nio.MaxDirectMemorySize
directMemory = Runtime.getRuntime().maxMemory()

堆外内存回收

三种方法:
1.达到限制触发自动回收;(system.gc()) // 可能被配置-XX:+DisableExplicitGC关闭。

2.手动调用UnsafefreeMemory接口。

3.使用DirectByteBuffer,它在初始化的时候会创建Cleaner这个Cleaner对象会在合适的时候执行unsafe.freeMemory(address),从而回收这块堆外内存。

4.手动调用((DirectBuffer)bb).cleaner().clean();.
// 内部还是调用System.gc(),所以一定不要-XX:+DisableExplicitGC

相关代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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.freeMemory(address);
address = 0;
Bits.unreserveMemory(size, capacity);
}
}

推荐文章