java的内存区域划分

  根据《Java虚拟机规范》的规定,运行时数据区通常包括这几个部分:

​    程序计数器(Program Counter Register)​

​    虚拟机栈(VM Stack)​

​    本地方法栈(Native Method Stack)​

​    方法区(Method Area)​

​    堆(Heap)​

  如下图: 

  Java内存模型_java虚拟机


  看到这个模型,使用堆和栈划分内存区域还是很有道理的,java虚拟机规范​将方法区描述为堆的一个逻辑部分​

  这种模型和我们粗略的内存模型的对应关系为:

​    堆=堆+方法区​  

    ​栈=虚拟机栈+本地方法栈​



  • 程序计数器
    线程隔离,即每个线程都有自己的程序计数器,并且互不影响。分为两种情况,当线程正在执行的是一个java方法,它的作用是作为字节码的行号指示器,指向下一条需要执行的指令。当线程正在执行的是一个​​Native方法​​,那么它的值为​​空(Undefined)​​。java虚拟机规范中唯一没有定义​​OOM(OutOfMemoryError)异常​​的内存区域。


  • java虚拟机栈
    线程隔离,生命周期与线程相同。它描述的是java方法执行的内存模型,每一个java方法执行的时候都会产生一个​​栈帧(Stack Frame)​​,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。当进入一个方法时,栈帧的大小是编译器确定的,运行时不会改变其大小。当虚拟机栈不可扩展的时候,可能抛出​​StackOverflowError异常​​,反之,可能抛出​​OOM异常​​。


  • 本地方法栈
    与java虚拟机栈功能一致,只不过本地方法栈是针对​​Native方法​​的。同样在虚拟机规范中定义了​​StackOverflowError​​和​​OOM​​两种异常。



Java堆

Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。
Java虚拟机规范中的描述为:​所有的对象实例以及数组都要在堆上分配。​
只要是用到​​new关键字​​创建的对象都会进入到这个区域,包括​​对象,数组​​。
堆还能进一步划分,比如按照内存回收的角度来看,堆可以进一步划分为​​新生代(Eden+Survivor)​​和​​老年代​​。按照内存分配的角度来看,堆可以进一步划分为多个线程私有的分配缓存区,即​​TLAB(Thread Local Allocation Buffer)​​。这种进一步的划分是为了更高效地回收和分配内存。java虚拟机规范中定义了​​OOM异常​​。



方法区

这个区域很容易引发误会,很多人会以为方法区会存储方法中的局部变量,然而并不是。方法区与Java堆一样,是各个线程共享的内存区域,这个区域用于存储被加载的类的信息,常量,静态变量以及即时编译器编译后的代码等数据。​Java虚拟机把方法区描述为堆得一个逻辑部分​。虚拟机规范中定义了​​OOM异常​​。还需要注意的一点是,HotSpot虚拟机中的方法区被很多人称之为“永生代”,这是因为HotSpot的开发团队将分代收集算法运用到了方法区,但是这并不是必须的。