1 定义
直接内存并不属于JVM的内存结构,它是物理机的内存,但是JVM虚拟机可以调用该部分内存。
直接内存的使用:
- 常见于
NIO
,用于数据缓冲区 - 分配回收的代价较高,但是速度很快
- 不收
JVM
内存回收管理
2 正常IO读取
从上图的结构图中,我们可以看到,当java
程序需要读取文件时,首先会在java堆内存中new
一个缓冲区,然后系统内存从磁盘中读取文件,再然后在将系统缓冲区中的字节流复制到java堆内存的缓冲区中,然后在由java程序调用。
这样做有一个缺点,就是需要开启两块内存,效率会很低。
3 直接内存读取
从图中可以看到,当java程序使用直接内存时,首先java程序在系统内存中分配一块直接内存块,这一内存块是系统内存和java堆内存可以共享的,那么系统内存读取到的磁盘文件就可以直接由java堆内存使用,这样就省去了复制的操作,大大节约了时间开销。
4 直接内存分配内存
通过java
中的unsafe
对象分配一块直接内存,直接内存大小在分配时指定。直接内存由于不受JVM
的管理,所以直接内存的释放,必须主动调用unsafe
对象进行释放,才能将直接内存释放。
这是ByteBuffer
的分配直接内存的源码:
1 | DirectByteBuffer(int cap) { // package-private |
cleaner = Cleaner.create(this, new Deallocator(base, size, cap));//释放直接内存
解释:这里使用cleaner对象用来释放直接内存,cleaner对象关联了当前的ByteBuffer对象,因为ByteBuffer对象是受java虚拟机管理的,直接内存不受java虚拟机管理,所以这里的关联,就是为了在当ByteBuffer被释放的时候,直接内存也被释放,只不过是被unsafe对象释放的,并不是Java虚拟机释放的。
cleaner对象是一个虚引用对象。
Deallocator类源码:
1 | private static class Deallocator |
5 直接内存回收
从第4节我们可以看到,直接内存的会随着ByteBuffer
对象的被回收,然后触发cleaner
对象,调用Unsafe
对象将直接内存回收,看起来也像是一种自动回收的方法。
但是,由于ByteBuffer
对象的回收,是遵循JVM
回收机制的,也就是说,得达到一定的回收条件才会回收ByteBuffer
对象。那么直接内存也不会被回收,这样就会导致内存不足。所以建议使用手动调用Unsafe
的方法释放直接内存。
写在最后
欢迎大家关注鄙人的公众号【麦田里的守望者zhg】,让我们一起成长,谢谢。