堆外内存和堆上内存
首先来讲一下什么是堆上内存,在java 中我们经常会编写类似下面一段代码
代码清单1-1
public class HeapByteBufferDemo { public static void main(String[] args) { Demo demo = new Demo();//1 demo.print(); }}class Demo{ void print() { System.out.println("i am a demo"); }}
1 处 使用new 关键字 去创建Demo对象,具体分为三步:
- 在jvm 运行时数据区中的堆上申请一块内存空间
- 初始化实例对象
- 把引用demo 指向分配的内存空间
此时 demo 引用指向的是堆上的一块内存空间,它由jvm管理的,同样也是gc 的主要工作区域。默认我们使用new 关键字,以及newInstance 都是在堆上申请内存。使用堆上的内存在多数情况下都是大家的首选,但是有些情况下我们使用堆外的内存就比较合适,什么是堆外内存呢,就是不被jvm 所管理的其他内存,简称堆外内存。
堆外内存也称直接内存,下面来说下使用直接内存的两个好处:
- 直接内存是在堆外,申请过多不会引起gc;例:申请一块堆外空间,当内存池去使用,netty 就是这种机制,有兴趣的同志可以去研究下,这也是我这个专栏将要涉及的地方。
- 在我们写数据的时候,若数据在堆上,则需要从堆上拷贝到堆外,操作系统才可以去操作这个拷贝的数据;若数据在堆外,就少了一次从堆上拷贝到堆外这个阶段了,节省的时间是非常明显的。大家可能比较纳闷为啥我们的操作系统属于内核态 在ring0级别按理说可以访问所有内存,为啥不直接操作堆上的数据,因为Java 有gc,gc 可能不会回收要被写的数据,但是可能会移动它(把已用内存压缩在一边,清除内存碎片),操作系统是通过内存地址去操作内存的,内存地址变了,这些写到文件或者网络里的数据可能并不是我们想要写的数据,也有可能产生很多未知的错误。
如何使用直接内存(这里使用上节的小例子)
代码清单2-1
/** * @author lzq */public class DirectBufferDemo { public static void main(String[] args) { ByteBuffer demoDirectByteBuffer = ByteBuffer.allocateDirect(8);//A printBufferProperties("write to demoDirectByteBuffer before