直接内存direct memory并不由jvm进行垃圾回收,可能导致内存泄漏问题。运行如下代码。
/**
* 演示直接内存溢出
*/
public class Demo1_10 {
static int _100Mb = 1024 * 1024 * 100;
public static void main(String[] args) {
List<ByteBuffer> list = new ArrayList<>();
int i = 0;
try {
while (true) {
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_100Mb);
list.add(byteBuffer);
i++;
}
} finally {
System.out.println(i);
}
// 方法区是jvm规范, jdk6 中对方法区的实现称为永久代
// jdk8 对方法区的实现称为元空间
}
}输出结果。
72
Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
at java.nio.Bits.reserveMemory(Bits.java:695)
at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)
at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)
at cn.itcast.jvm.t1.direct.Demo1_10.main(Demo1_10.java:19)直接内存的底层回收机制是怎样的呢?运行以下代码。
/**
* 禁用显式回收对直接内存的影响
*/
public class Demo1_26 {
static int _1Gb = 1024 * 1024 * 1024;
/*
* -XX:+DisableExplicitGC 显式的
*/
public static void main(String[] args) throws IOException {
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_1Gb);
System.out.println("分配完毕...");
System.in.read();
System.out.println("开始释放...");
byteBuffer = null;
System.gc(); // 显式的垃圾回收,Full GC
System.in.read();
}
}在控制台输出分配完毕后,从后台的任务管理器可以看到其内存占用情况。
当在控制台输入回车,输出开始释放,再输入回车,这个占用1个G的内存的进程就被清理了。是不是意味着java的gc操作发生了作用呢?
下面我们来解析上面直接内存回收的过程。Unsafe是jdk底层的一个类,用于内存分配,内存回收等,一般普通程序员无需使用,这里我们通过反射获取Unsafe对象,演示直接内存分配的底层原理。
/**
* 直接内存分配的底层原理:Unsafe
*/
public class Demo1_27 {
static int _1Gb = 1024 * 1024 * 1024;
public static void main(String[] args) throws IOException {
Unsafe unsafe = getUnsafe();
// 分配内存
long base = unsafe.allocateMemory(_1Gb);
unsafe.setMemory(base, _1Gb, (byte) 0);
System.in.read();
// 释放内存
unsafe.freeMemory(base);
System.in.read();
}
public static Unsafe getUnsafe() {
try {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);
return unsafe;
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}运行代码,在任务管理器观察jdk进程内存占用发现,内存占用会在allocateMemory()后增加1G,在freeMemory()后恢复。因此,直接内存的回收其实不是由jvm虚拟机完成,而是通过Unsafe对象调用freeMemory()完成。

京公网安备 11010502036488号