热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

NIODirectByteBuffer内存泄露的测试

  写NIO程序经常使用ByteBuffer来读取或者写入数据,那么使用ByteBuffer.allocate(capability)还是使用ByteBuffer.allocteDirect(ca

    写NIO程序经常使用ByteBuffer来读取或者写入数据,那么使用ByteBuffer.allocate(capability)还是使用ByteBuffer.allocteDirect(capability)来分配缓存了?第一种方式是分配JVM堆内存,属于GC管辖范围,由于需要拷贝所以速度相对较慢;第二种方式是分配OS本地内存,不属于GC管辖范围,由于不需要内存拷贝所以速度相对较快。

 

    我们肯定想选择比较快的,但问题是直接内存不属于GC管辖范围,需要弄清楚这部分内存如何管理,否则造成内存泄露就麻烦了。本地内存在JAVA中有一个对应的包装类DirectByteBuffer,该类属于Java类,适当的时候会被GC回收,当它被回收前会调用本地方法把直接内存给释放了,所以本地内存可以随DirectByteBuffer对象被回收而自动回收,貌似没有问题;但如果不断分配本地内存,堆内存很少使用,那么JVM就不需要执行GC,DirectByteBuffer对象们就不会被回收,这时候堆内存充足,但本地内存可能已经使用光了,再次尝试分配本地内存就会出现OutOfMemoryError,那程序就直接崩溃了。

 

    有没有解决方案?自动释放不靠谱,我们是否可以手动释放本地内存,把握主动权?果然DirectByteBuffer持有一个Cleaner对象,该对象有一个clean()方法可用于释放本地内存,所以需要的时候我们可以调用这个方法手动释放本地内存。

 

以下代码与测试场景帮助理解与证实以上描述。

 

代码1:

123456789101112131415 package com.stevex.app.nio; import java.nio.ByteBuffer;import java.util.concurrent.TimeUnit; public class DirectByteBufferTest {    public static void main(String[] args) throws InterruptedException{            //分配128MB直接内存        ByteBuffer bb = ByteBuffer.allocateDirect(1024*1024*128);                 TimeUnit.SECONDS.sleep(10);        System.out.println("ok");    } }

 

测试用例1:设置JVM参数-Xmx100m,运行异常,因为如果没设置-XX:MaxDirectMemorySize,则默认与-Xmx参数值相同,分配128M直接内存超出限制范围。

12345 Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory    at java.nio.Bits.reserveMemory(Bits.java:658)    at java.nio.DirectByteBuffer.(DirectByteBuffer.java:123)    at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:306)    at com.stevex.app.nio.DirectByteBufferTest.main(DirectByteBufferTest.java:8)

 

测试用例2:设置JVM参数-Xmx256m,运行正常,因为128M小于256M,属于范围内分配。

1  

 

测试用例3:设置JVM参数-Xmx256m -XX:MaxDirectMemorySize=100M,运行异常,分配的直接内存128M超过限定的100M。

12345 Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory    at java.nio.Bits.reserveMemory(Bits.java:658)    at java.nio.DirectByteBuffer.(DirectByteBuffer.java:123)    at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:306)    at com.stevex.app.nio.DirectByteBufferTest.main(DirectByteBufferTest.java:8)

 

代码2:

12345678910111213141516171819202122 package com.stevex.app.nio; import java.nio.ByteBuffer;import java.util.concurrent.TimeUnit;import sun.nio.ch.DirectBuffer; public class DirectByteBufferTest {    public static void main(String[] args) throws InterruptedException{        //分配512MB直接缓存        ByteBuffer bb = ByteBuffer.allocateDirect(1024*1024*512);                 TimeUnit.SECONDS.sleep(10);                 //清除直接缓存        ((DirectBuffer)bb).cleaner().clean();                 TimeUnit.SECONDS.sleep(10);                 System.out.println("ok");    } }

测试用例4:设置JVM参数-Xmx768m,运行程序观察内存使用变化,会发现clean()后内存马上下降,说明使用clean()方法能有效及时回收直接缓存。


推荐阅读
author-avatar
我是个密_916
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有