直接内存是操作系统内存;不属于JVM部分
直接内存定义
- 常见于NIO操作时,用于数据缓冲区
- 分配回收成本较高,但读写性能高
- 不受JVM内存回收管理
直接内存的基本使用
使用 io 函数进行读写文件后:
用时大约2921,过程:磁盘文件首先进入到系统缓冲区,但是系统缓冲区中Java的代码是不可以运行的,所以Java堆缓冲区中会划分一块Java缓冲区,然后再将Java堆缓冲区再去访问系统缓冲区中的内容,这就出现一个问题,因为有两块缓冲区,所以,第一次在系统缓冲区中还不行,因为Java访问不到,所以要复制一份给Java缓冲区,这就造成了不必要数据的复制;
使用 directBuffer 函数后,运行速度明显变快了;
用时大约892,过程:会在操作系统中划分出一块内存directBuffer,这块内存系统内存可以访问,Java堆内存也可以访问,这就是直接内存,这比刚刚少了复制的操作,这样速度也会得到很大的提升;
直接内存的出现,使得Java代码和系统内存都可以进行访问,省去了第一张图中分别开辟两块缓冲区内存,如此速度得到了提升;直接内存比较适合做文件的操作;
例题:演示直接内存溢出
输出36,提示信息:java.lang.outOfMemoryError: Direct buffer memory
直接内存的释放原理
例题:演示直接内存释放
这里查看内存大小不能使用前面的JVM工具,要使用任务管理器进行查看;
运行之后发现结果和上一张图一致,说明直接内存的内存释放,并不是通过垃圾回收器进行回收的,而是通过 unsafe 进行回收的;Java中的垃圾回收不需要我们去手动释放内存,它会自动释放内存,但是在直接内存中,它不会自动释放内存,需要手动调用 unsafe 中的 freeMemory 方法进行内存释放;
总结:分配和回收原理
- 使用了Unsafe对象完成直接内存的分配回收,并且回收需要主动调用freeMemory方法
- ByteBuffer的实现类内部,使用了Cleaner(虚引用)来监测ByteBuffer对象,一旦ByteBuffer对象被垃圾回收,那么就会由ReferenceHandler线程通过Cleaner的clean方法调用freeMemory来释放直接内存
禁用显式回收对直接内存的影响
因为此时不能进行垃圾回收,只能借助直接内存来释放内存;