Java 虚拟机在执行Java程序的过程当中会把它在主存中管理的内存部分划分红多个区域,每一个区域存放不一样类型的数据。下图所示为java虚拟机运行的时候,主要的内存分区:java
在这些分区中,占用内存空间最大的一部分叫作“堆(heap)”,也就是咱们所说的堆内内存(on-heap memory)。java虚拟机中的“堆”主要是存放全部对象的实例。这一块区域在java虚拟机启动的时候被建立,被全部的线程所共享,同时也是垃圾收集器的主要工做区域,所以这一部分区域除了被叫作“堆内内存”之外,也被叫作“GC堆”(Garbage Collected Heap)。web
堆内内存是java垃圾收集器的主要工做区域,为了提升垃圾回收的效率,在堆内内存的内部又划分出了新生代、老年代和永久代。在新生代内存中又按照8:1:1的比例(java虚拟机默认分配比例为8:1:1,这个比例也能够自定义)划分出了Eden, Survivor1, Survivor2三个区域。算法
在执行垃圾回收算法的时候,不一样的回收算法会对内存区域形成不同的影响。可是大部分的回收算法会形成堆内内存空间在物理上的不连续性。下面以最基本的垃圾回收算法“标记 - 清除算法”为例:数据结构
能够看到,内存区域在通过垃圾回收以后,产生大量不连续的内存空间。所以,java虚拟机中的堆内内存区域,只是逻辑上的连续,并不能保证物理上的连续性。 因此,操做系统并不能直接获得堆内内存区域所存储的数据在主存中的正确地址。在一些特定的时间点,Java虚拟机会进行一次完全的垃圾回收(full gc)。完全回收时,垃圾收集器会对全部分配的堆内内存进行完整的扫描,在扫描期间,绝大部分正在运行的java线程都会被暂时中止。这意味着:这样一次垃圾收集对Java应用形成的影响,跟堆内内存所存储的数据的多少是成正比的,过大的堆内内存会影响Java应用的性能。svg
为了解决堆内内存过大带来的长时间的GC停顿的问题,以及操做系统对堆内内存不可知的问题,java虚拟机开辟出了堆外内存(off-heap memory)。堆外内存意味着把一些对象的实例分配在Java虚拟机堆内内存之外的内存区域,这些内存直接受操做系统(而不是虚拟机)管理。这样作的结果就是能保持一个较小的堆,以减小垃圾收集对应用的影响。同时由于这部分区域直接受操做系统的管理,别的进程和设备(例如GPU)能够直接经过操做系统对其进行访问,减小了从虚拟机中复制内存数据的过程。性能
java 在NIO 包中提供了ByteBuffer类,对堆外内存进行访问。下图为NIO包中ByteBuffer的层次继承关系spa
使用下面的方式,能够直接开辟指定大小的对外内存:操作系统
import sun.nio.ch.DirectBuffer;
import java.nio.ByteBuffer;
public class TestDirectByteBuffer {
public static void main(String[] args) throws Exception {
while (true) {
ByteBuffer buffer = ByteBuffer.allocateDirect(10 * 1024 * 1024);
}
}
}
这样咱们就开辟出了一块大小为10M的堆外内存。线程
优势 :code
缺点 :
虽然堆外内存自己不受垃圾回收算法的管辖,可是由于其是由ByteBuffer所创造出来的,所以这个buffer自身做为一个实例化的对象,其自身的信息(例如堆外内存在主存中的起始地址等信息)必须存储在堆内内存中,具体状况以下图所示。
当在堆内内存中存放的buffer对象实例被垃圾回收算法回收掉的时候,这个buffer对应的堆外内存区域同时也就被释放掉了。