根据《java虚拟机规范第二版》规定,现阶段的java内存区域总体如下图
其中,方法区和堆是所有线程共享区域。 虚拟机栈,本地方法栈,程序计数器是各线程独占。
概述一下各个区域
先说说线程私有的几个区域:
1.程序计数器
1.当前线程所执行的字节码的行号指示器,通过改变这个计数器的值来选取下一条需要执行的指令。由于java虚拟机多线程通过线程切换轮流执行,所以为了线程切换以后能恢复到正确的位置开始执行,所以每一条线程都有自己的程序计数器。
2.其中:如果当前线程执行的是java方法,那么记录器记录的是虚拟机字节码指令的地址,如果是native方法,那么这个值为空。
3.它是java虚拟机规范中唯一一个没有定义OutOfMemoryError情况的区域。
2.java虚拟机栈
1.每个方法执行时都会同时创建一个帧栈,用于存储局部变量表,操作栈,动态链接,方法出口等信息。每一个方法被调用直至执行完成的过程,就对应一个帧栈在虚拟机中从入栈到出栈的过程。
2.常说的“堆栈”中的栈就是指java虚拟机栈中的局部变量表部分。
3.局部变量表中存储的是基本类型,对象引用(句柄,或者引用指针),和returnAddress。64为的long和double会占用2个局部变量空间slot。其余占用1个。局部变量所需的内存空间在编译器的时候完成,当进入一> 个方法时,这个方法在帧中占用多少空间是完全确定的。在方法运行期间不会改变局部变量表的大小。
异常:
- 1.当线程请求的栈深度大于虚拟机所允许的深度,将抛出*Error。
- 2.当虚拟机栈可以动态扩展,无法申请到足够的内存时就会抛出OutOfMemoryError。
3.本地方法栈
作用和虚拟机栈类似,区别在于本地方法栈是为native方法服务的,由虚拟机自行实现,有些虚拟机如sun hotspot将本地方法栈和虚拟机栈合并。他也会抛出异常。
线程共享的区域:
1.java堆
唯一目的就是存储对象实例
java虚拟机规范:java堆可以处于物理不连续的内存中,只要逻辑上市连续的即可。
2.方法区(non-heap)
存储虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。属于堆的一个逻辑部分,为了便于区分,别名:non-heap
该内存也存在内存回收机制。
运行常量池: 方法区的一部分,用于存储编译时生成的字面量和符号引用。运行期间也可能将新的常量放入池中,常用的有string.Intern();
另外说一个不属于java内存区域内的:直接内存
JDK1.4加入的nio采用native方法直接操作本机内存。然后再java堆里面创建一个directbytebuffer对象作为这块内存的引用。但是如果使用不当,会使得堆外内存超过物理内存而导致内存溢出。
另外说说对象的访问
主流的有两种,分别是句柄和指针。