java学习-【转】NIO DirectByteBuffer 内存泄露的测试

程老师原文地址:http://flychao88.iteye.com/blog/2188489

原文如下:

 写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:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

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直接内存超出限制范围。

1

2

3

4

5

Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory

     at java.nio.Bits.reserveMemory(Bits.java:658)

     at java.nio.DirectByteBuffer.<init>(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。

1

2

3

4

5

Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory

     at java.nio.Bits.reserveMemory(Bits.java:658)

     at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)

     at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:306)

     at com.stevex.app.nio.DirectByteBufferTest.main(DirectByteBufferTest.java:8)


代码2:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

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()方法能有效及时回收直接缓存。

**************************原文结束,学习笔记开始********************************

又是NIO啊,大神就是精通这些高级技能,好吧,我照例跑了遍代码。

想到一个知识点:这是操作非堆内存,优点是提高效率,缺点嘛需要自己管理,没有GC容易OOM。对应参数:-XX:MaxDirectMemorySize

,默认是64M。还有之前学习笔记有篇就是SharedHashMap

注意点2: 如果你要使用direct buffer,一定不要加上DisableExplicitGC这个参数,因为这个参数会把你的System.gc()视作空语句,最后很容易导致OOM。(为啥呢?因为见下图,源码里是有 System.gc(),禁止了就不能回收,容易触发溢出 )

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值