【Java】 DirectByteBuffer堆外内存回收( 五 )

reserveMemory内存清理最开始在DirectByteBuffer的构造函数中看到申请内存之前会调用Bits的reserveMemory方法,如果没有足够的内存,它会从SharedSecrets获取JavaLangRefAccess对象进行一些处理,由前面的内容可知 , Reference中的静态方法启动ReferenceHandler之后,创建了JavaLangRefAccess并设置到SharedSecrets中,所以这里调用JavaLangRefAccesstryHandlePendingReference实际上依旧调用的是Reference中的tryHandlePending方法 。
在调用Reference中的tryHandlePending方法处理需要回收的对象之后,调用tryReserveMemory方法判断是否有足够的内存 , 如果内存依旧不够,会调用` System.gc()触发垃圾回收,然后开启一个循环,处理逻辑如下:

  1. 判断内存是否充足,如果充足直接返回;
  2. 判断睡眠次数是否小于限定的最大值,如果小于继续下一步 , 否则终止循环;
  3. 调用tryHandlePendingReference处理penging列表中的引用对象,前面在处理pending列表的逻辑中可以知道 , 如果pending列表不为空 , 会返回true,tryHandlePendingReference也会返回true,此时意味着清理了一部分对象,所以重新进入到第1步进行检查;
    如果pending列表为空 , 会返回参数中传入的waitForNotify的值 , 从JavaLangRefAccess的tryHandlePendingReference中可以看出这里传入的是false , 所以会进行如下处理:
    • 通过Thread.sleep(sleepTime)让当前线程睡眠一段时间 , 这样可以避免reserveMemory方法一直在占用资源;
    • 对睡眠次数加1;
  4. 如果以上步骤处理之后还没有足够的空间会抛出抛出OutOfMemoryError异常;
reserveMemory方法的作用是保证在申请内存之前有足够的内存,如果没有足够的内存会进行清理 , 达到指定清理次数之后依旧没有足够的内存空间,将抛出OutOfMemoryError异常 。
class Bits {static void reserveMemory(long size, int cap) {if (!memoryLimitSet && VM.isBooted()) {maxMemory = VM.maxDirectMemory();memoryLimitSet = true;}// 是否有足够内存if (tryReserveMemory(size, cap)) {return;}// 获取JavaLangRefAccessfinal JavaLangRefAccess jlra = SharedSecrets.getJavaLangRefAccess();// 调用tryHandlePendingReferencewhile (jlra.tryHandlePendingReference()) {// 判断是否有足够的内存if (tryReserveMemory(size, cap)) {return;}}// 调用gc进行垃圾回收System.gc();boolean interrupted = false;try {long sleepTime = 1;int sleeps = 0;// 开启循环while (true) {// 是否有足够内存if (tryReserveMemory(size, cap)) {return;}// 如果次数小于最大限定次数,终止if (sleeps >= MAX_SLEEPS) {break;}// 再次处理penging列表中的对象if (!jlra.tryHandlePendingReference()) {try {// 睡眠一段时间Thread.sleep(sleepTime);sleepTime <<= 1;sleeps++; // 睡眠次数增加1} catch (InterruptedException e) {interrupted = true;}}}// 抛出OutOfMemoryError异常throw new OutOfMemoryError("Direct buffer memory");} finally {if (interrupted) {// don't swallow interruptsThread.currentThread().interrupt();}}}}public abstract class Reference<T> {static {// ...// 这里设置了JavaLangRefAccessSharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {@Overridepublic boolean tryHandlePendingReference() {// 调用tryHandlePending,这里waitForNotify参数传入的是falsereturn tryHandlePending(false);}});}}参考
Reference源码解析
一文读懂java中的Reference和引用类型
Java 源码剖析——彻底搞懂 Reference 和 ReferenceQueue
【【Java】 DirectByteBuffer堆外内存回收】

推荐阅读