java之HeapByteBuffer&DirectByteBuffer以及回收DirectByte

byte buffer一般在网络交互过程中java使用得比较多,尤其是以NIO的框架中;

看名字就知道是以字节码作为缓冲的,先buffer一段,然后flush到终端。

而本文要说的一个重点就是HeapByteBuffer与DirectByteBuffer,以及如何合理使用DirectByteBuffer。

1、HeapByteBufferDirectByteBuffer,在原理上,前者可以看出分配的buffer是在heap区域的,其实真正flush到远程的时候会先拷贝得到直接内存,再做下一步操作(考虑细节还会到OS级别的内核区直接内存),其实发送静态文件最快速的方法是通过OS级别的send_file,只会经过OS一个内核拷贝,而不会来回拷贝;在NIO的框架下,很多框架会采用DirectByteBuffer来操作,这样分配的内存不再是在java heap上,而是在C heap上,经过性能测试,可以得到非常快速的网络交互,在大量的网络交互下,一般速度会比HeapByteBuffer要快速好几倍。

最基本的情况下

分配HeapByteBuffer的方法是:

[java] view plaincopy

  1. ByteBuffer.allocate(int capacity);参数大小为字节的数量

分配DirectByteBuffer的方法是:

[java] view plaincopy

  1. ByteBuffer.allocateDirect(int capacity);//可以看到分配内存是通过unsafe.allocateMemory()来实现的,这个unsafe默认情况下java代码是没有能力可以调用到的,不过你可以通过反射的手段得到实例进而做操作,当然你需要保证的是程序的稳定性,既然叫unsafe的,就是告诉你这不是安全的,其实并不是不安全,而是交给程序员来操作,它可能会因为程序员的能力而导致不安全,而并非它本身不安全。

由于HeapByteBufferDirectByteBuffer类都是default类型的,所以你无法字节访问到,你只能通过ByteBuffer间接访问到它,因为JVM不想让你访问到它,对了,JVM不想让你访问到它肯定就有它不可告人的秘密;后面我们来跟踪下他的秘密吧。

2、前面说到了,这块区域不是在java heap上,那么这块内存的大小是多少呢?默认是一般是64M,可以通过参数:-XX:MaxDirectMemorySize来控制,你够牛的话,还可以用代码控制,呵呵,这里就不多说了。

3、直接内存好,我们为啥不都用直接内存?请注意,这个直接内存的释放并不是由你控制的,而是由full gc来控制的,直接内存会自己检测情况而调用system.gc(),但是如果参数中使用了DisableExplicitGC 那么这是个坑了,所以啊,这玩意,设置不设置都是一个坑坑,所以java的优化有没有绝对的,只有针对实际情况的,针对实际情况需要对系统做一些拆分做不同的优化。

4、那么full gc不触发,我想自己释放这部分内存有方法吗?可以的,在这里没有什么是不可以的,呵呵!私有属性我们都任意玩他,还有什么不可以玩的;我们看看它的源码中DirectByteBuffer发现有一个:Cleaner,貌似是用来搞资源回收的,经过查证,的确是,而且又看到这个对象是sun.misc开头的了,此时既惊喜又郁闷,呵呵,只要我能拿到它,我就能有希望消灭掉了;下面第五步我们来做个试验。

5、因为我们的代码全是私有的,所以我要访问它不能直接访问,我需要通过反射来实现,OK,我知道要调用cleaner()方法来获取它Cleaner对象,进而通过该对象,执行clean方法;(付:以下代码大部分也取自网络上的一篇copy无数次的代码,但是那个代码是有问题的,有问题的部分,我将用红色标识出来,如果没有哪条代码是无法运行的

[java] view plaincopy

  1. import java.nio.ByteBuffer;
  2. import sun.nio.ch.DirectBuffer;
  3. public class DirectByteBufferCleaner {
  4. public static void clean(final ByteBuffer byteBuffer) {
  5. if (byteBuffer.isDirect()) {
  6. ((DirectBuffer)byteBuffer).cleaner().clean();
  7. }
  8. }
  9. }

上述类你可以在任何位置建立都可以,这里多谢一楼的回复,以前我的写法是见到DirectByteBuffer类是Default类型的,因此这个类无法直接引用到,是通过反射去找到cleaner的实例,进而调用内部的clean方法,那样做麻烦了,其实并不需要那么麻烦,因为DirectByteBuffer implements了DirectBuffer,而DirectBuffer本身是public的,所以通过接口去调用内部的Clear对象来做clean方法。

我们下面来做测试来证明这个程序是有效地回收的:

在任意一个地方写一段main方法来调用,我这里就直接写在这个类里面了:

[java] view plaincopy

  1. public static void sleep(long i) {
  2. try {
  3. Thread.sleep(i);
  4. }catch(Exception e) {
  5. /*skip*/
  6. }
  7. }
  8. public static void main(String []args) throws Exception {
  9. ByteBuffer buffer = ByteBuffer.allocateDirect(1024 * 1024 * 100);
  10. System.out.println("start");
  11. sleep(10000);
  12. clean(buffer);
  13. System.out.println("end");
  14. sleep(10000);
  15. }

这里分配了100M内存,为了将结果看清楚,在执行前,执行后分别看看延迟10s,当然你可以根据你的要求自己改改。请提前将OS的资源管理器打开,看看当前使用的内存是多少,如果你是linux当然是看看free或者用top等命令来看;本地程序我是用windows完成,在运行前机器的内存如下图所示:

开始运行在输入start后,但是未输出end前,内存直接上升将近100m。

在输入end后发现内存立即降低到2.47m,说明回收是有效的。

此时可以观察JVM堆的内存,不会有太多的变化,注意:JVM本身启动后也有一些内存开销,所以不要将那个开销和这个绑定在一起;这里之所以一次性申请100m也是为了看清楚过程,其余的可以做实验玩玩了。

java之HeapByteBuffer&DirectByteBuffer以及回收DirectByte

时间: 2024-08-09 19:52:49

java之HeapByteBuffer&DirectByteBuffer以及回收DirectByte的相关文章

Java内存组成和垃圾回收机制

眼看就要到找工作的时候了,平时在实验室也做了不少项目,可到头来,假设面试官问我平时做过什么,我确不知从何说起,也可以说我不知道说什么.前辈们早就说过,计算机这个行业需要不断的学习,也需要不断的积累,自问平时遇到过不少问题,也解决了不少问题,可到头来,好像都没什么印象了!在准备找工作的时候,就将平时一些研究过的,倒腾过的重新记录下吧!由于本人是第一次写博客,文笔不太好,内容可能也有很多借鉴了是前辈们的,但重在重新整理.精选,也让自己在整理的过程中重新学习,加深印象! 一.内存组成 在我做项目的时候

Java基础之详细理解回收机制

在以前从事C/C++开发的时候,内存的管理一直是需要被谨慎考虑的内容.在C语言中,我们使用库函数malloc()和free()两个库函数来实现从堆中分配内存与释放,而C++则使用操作符new和delete来实现内存的管理,对于这两个方式,后者是操作符而前者是库函数,后者能够被编译器处理而前者着重于对内部数据实现构造,在面向对象设计中,后者能更好的结合构造函数对自定义对象实现内存分配.但是,在接触了Java之后,我们在内存的管理上可以轻松许多,关键是Java实现了内存的自动管理模式,具体是怎么样的

77.JAVA编程思想——模拟垃圾回收

77.JAVA编程思想--模拟垃圾回收 这个问题的本质是若将垃圾丢进单个垃圾筒,事实上是未经分类的.但在以后,某些特殊的信息必须恢复,以便对垃圾正确地归类.在最开始的解决方案中,RTTI 扮演了关键的角色.这并不是一种普通的设计,因为它增加了一个新的限制.正是这个限制使问题变得非常有趣--它更象我们在工作中碰到的那些非常麻烦的问题.这个额外的限制是:垃圾抵达垃圾回收站时,它们全都是混合在一起的.程序必须为那些垃圾的分类定出一个模型.这正是RTTI 发挥作用的地方:我们有大量不知名的垃圾,程序将正

HeapByteBuffer和DirectByteBuffer以及回收DirectByteBuffer

由于HeapByteBuffer和DirectByteBuffer类都是default类型的,所以你无法字节访问到,你只能通过ByteBuffer间接访问到它,因为JVM不想让你访问到它. 分配HeapByteBuffer的方法是: ByteBuffer.allocate(int capacity);参数大小为字节的数量 分配DirectByteBuffer的方法是: ByteBuffer.allocateDirect(int capacity);//可以看到分配内存是通过unsafe.allo

nio DirectByteBuffer如何回收堆外内存

概述 使用了nio框架的应用,比如服务框架,利用nio建立长连接通信,他们会使用DirectByteBuffer来分配堆外内存,也就是本地直接内存,这个内存的回收不由gc直接维护,我们通常所说的gc,只回收jvm的堆.栈.方法区.本地内存如果没有用jvm启动参数手动指定,它会根据主机的剩余可用内存进行分配,如果说一个机器的8G内存的,其中,我们手动指定的jvm堆.方法区内存为2048 + 256,那么,除了其他进程占用的内存,剩余的可用内存可能是较大的.如果你的主机有内存使用量监控(不是jvm级

Java内存分配与垃圾回收

1.JVM管理的内存包含下图所示的几个运行时数据区域,其中方法区和堆为线程共享的数据区域,程序计数器,虚拟机栈以及本地方法栈为线程私有的数据区域. 程序计数器:可以看做是当前线程所执行的字节码的行号指示器,告诉字节码解释器该读取哪条指令 虚拟机栈:生命周期和线程相同,每个方法在执行的同时都会创建一个栈帧,用于存储局部变量表,操作数栈,动态链接,方法出口等信息,每一个方法从调用到完成的过程就对应了一个栈帧在虚拟机中入栈到出栈的过程.栈中存放了编译器可知的各种基本数据类型和对象引用. 本地方法栈:与

Java内存管理及垃圾回收总结

概述 Java和C++的一个很重要的差别在于对内存的管理.Java的自己主动内存管理及垃圾回收技术使得Java程序猿不须要释放废弃对象的内存.从而简化了编程的过程.同一时候也避免了因程序猿的疏漏而导致的内存泄露问题. 内存管理和垃圾回收是JVM很重要的一个部分.深入理解Java的内存管理和垃圾回收机制是避免及修复Java相关异常(OutOfMemoryError, StackOverflowError),理解Java对象创建过程,有效利用内存.构建高性能Java应用的前提.本文将先后介绍Java

JVM学习(4)——全面总结Java的GC算法和回收机制---转载自http://www.cnblogs.com/kubixuesheng/p/5208647.html

俗话说,自己写的代码,6个月后也是别人的代码--复习!复习!复习!涉及到的知识点总结如下: 一些JVM的跟踪参数的设置 Java堆的分配参数 -Xmx 和 –Xms 应该保持一个什么关系,可以让系统的性能尽可能的好呢?是不是虚拟机内存越大越好? Java 7之前和Java 8的堆内存结构 Java栈的分配参数 GC算法思想介绍 –GC ROOT可达性算法 –标记清除 –标记压缩 –复制算法 可触及性含义和在Java中的体现 finalize方法理解 Java的强引用,软引用,弱引用,虚引用 GC

Java内存模型与垃圾回收

1.Java内存模型 Java虚拟机在执行程序时把它管理的内存分为若干数据区域,这些数据区域分布情况如下图所示: 程序计数器:一块较小内存区域,指向当前所执行的字节码.如果线程正在执行一个Java方法,这个计数器记录正在执行的虚拟机字节码指令的地址,如果执行的是Native方法,这个计算器值为空. Java虚拟机栈:线程私有的,其生命周期和线程一致,每个方法执行时都会创建一个栈帧用于存储局部变量表.操作数栈.动态链接.方法出口等信息. 本地方法栈:与虚拟机栈功能类似,只不过虚拟机栈为虚拟机执行J