定义
Direct Memory,不属于jvm内存,属于操作系统
- 常见于NIO操作,用于数据缓冲区
- 分配回收成本高,但读写性能高
- 直接内存不受JVM内存回收管理
文件拷贝测试直接内存速度
public class Main{
static final String FROM = "C:\\Users\\lenovo\\Videos\\Captures\\uni-zuaflea_pages_index_index.vue - HBuilder X 3.2.16 2022-01-14 15-11-00.mp4";
static final String TO = "D:\\a.mp4";
static final int _1M = 1024*1024;
public static void main(String[] args){
directBuffer();
io();
}
private static void directBuffer(){
long start = System.nanoTime();
try(
FileChannel from = new FileInputStream(FROM).getChannel();
FileChannel to = new FileOutputStream(TO).getChannel();
){
ByteBuffer buffer = ByteBuffer.allocateDirect(_1M);
while(true){
int len = from.read(buffer);
if(len==-1){
break;
}
buffer.flip();
to.write(buffer);
buffer.clear();
}
}catch (IOException e){
e.printStackTrace();
}
long end = System.nanoTime();
System.out.println("直接内存用时:"+(end-start)/1000);
}
private static void io(){
long start = System.nanoTime();
try(
FileInputStream from = new FileInputStream(FROM);
FileOutputStream to = new FileOutputStream(TO);
){
byte[] buffer = new byte[_1M];
while(true){
int len = from.read(buffer);
if(len==-1){
break;
}
to.write(buffer,0,len);
}
}catch (IOException e){
e.printStackTrace();
}
long end = System.nanoTime();
System.out.println("普通io用时:"+(end-start)/1000);
}
}
磁盘读写原理
一般IO
NIO
使用直接内存之后,Java和操作系统都可以访问这一块区域,相当于将两个缓冲区合并了,IO性能提升
内存溢出
public class Main{
static int _100M = 100 * 1024 * 1024;
public static void main(String[] args) {
List<ByteBuffer> list = new ArrayList<>();
int i=1;
try{
while(true){
i++;
list.add(ByteBuffer.allocateDirect(_100M));
}
}catch (Throwable e){
e.printStackTrace();
}finally {
System.out.println(i);
}
}
}
内存分配和释放原理
Java对直接内存的分配和释放,使用的是Unsafe对象
Unsafe申请内存案例
public class Main{
static final int _1GB = 1024 * 1024 * 1024;
public static void main(String[] args) {
Unsafe unsafe = getUnsafe();
// 申请直接内存,base是申请到的直接内存的地址
long base = unsafe.allocateMemory(_1GB);
// 设置直接内存
unsafe.setMemory(base,_1GB,(byte) 0);
// 释放base指向的直接内存
unsafe.freeMemory(base);
}
/**
* Unsafe类仅仅有一个private的构造器,不希望开发者使用Unsafe,可以通过反射来获取类内部的私有Unsafe对象
* 通过反射获取Unsafe对象
* @return
*/
private static Unsafe getUnsafe(){
try{
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
return unsafe;
}catch (Exception e){
throw new RuntimeException(e);
}
}
}
源码分析
分配和回收原理总结
- 使用了Unsafe对象完成直接内存的分配和回收,并且回收需要主动调用freeMemory方法
- ByteBuffer的实现类内部,使用了Cleaner(虚引用)来检测ByteBuffer对象,一旦ByteBuffer对象被垃圾回收,那么就会由ReferenceHandler线程通过Cleaner的clean方法调用freeMemory来释放直接内存