7 直接内存
7.1 定义和介绍
直接内存属于系统内存,并不属于jvm。属于操作系统内存管理。
-
常见于BIO操作时,用于数据缓冲区
-
分配回收成本较高,但读写性能高
-
不受JVM内存回收管理
使用传统的io和使用直接内存读取一个800M大小的文件对比
思考:为什么使用直接内存,我们大文件的读写效率会这么高?
先了解文件读写过程,java要调用操作系统提供函数才能读写。那么cpu的状态会从用户态变为内核态。当切换到内核态的时候,由cpu的函数去读取磁盘文件内容。他会在操作系统内存中划出一块系统缓冲区,磁盘的内容会先读到这个系统缓冲区。不肯把800M的东西直接读到内存,那内存会顶不住。利用缓冲区分次读取。注意系统缓冲器java不能读取,所以java在堆区划分一块java缓冲区,要想读取到数据,就把数据从系统缓冲区读到java缓冲区。之后cpu进入用户态,去调用输出流的写入操作,反复读写把文件复制。
如果使用直接内存:当我们调用allocateDirect(_1Mb),分配一块直接内存,代表着我会 在操作系统划出一块直接内存区,这块内存系统可以直接用,java代码也可以直接用,是共享的。比刚才的少一次缓冲区复制操作 ,效率提高。
7.2 内存溢出
因为不会被JVM管理,GC也回收不到这块内存。那会存在直接内存溢出的情况。
7.3 内存释放原理
直接内存在底层的分配和释放是通过unsafe管理的,一般jdk内部人员用到unsafe,我们用不着。
那么bytebuffer如何和unsafe关联起来的呢?
后台有一个线程监控着,虚引用机制,如果没人用直接内存了,那就主动执行任务去释放直接内存。
7.4 禁用显示回收对直接内存的影响
我们在JVM调优的时候,通常会加这句,让我们GC不工作
-XX:+DisableExplicitGC
System.GC()他不仅会回收新生代,还会回收老年代,造成程序暂停的时间比较长。为了防止一些程序员不小心写system.GC()触发垃圾回收影响性能。
一旦禁用system.GC()以后啊,那我们bytebuffer关联的直接内存也不会被释放掉。造成直接内存长时间占用。那怎么办呢?
可以用unsafe对象去调用freeMemory去释放直接内存
手动管理这块直接内存