java内存区域
总述:
java虚拟机运行时数据区
一、程序计数器
如果线程正在运行的是一个java方法,则PC记录的是正在执行的字节码指令的地址。
如果线程正在运行的是一个Natvie方法,则PC为空。
此区域是java虚拟机中唯一一个没有规定任何OutOfMemory情况的区域。
二、java虚拟机栈
其生命周期与线程相同 ,描述的是Java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧(Stack Frame)。
用于存储局部变量表,操作数栈,动态链接,方法的出口信息等。
每一个方法的调用直到执行完,对应一个 Stack Frame 在虚拟机栈中入栈 出栈的过程 。示意图职下
局部变量表存放的了编译期可知的各种基本类型(boolean ,byte ,char ,short, int ,float, long ,double),对象的引用(reference类型 ),returnAddress类型(指向了一条字节码指令的地址).
注意:reference类型,不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是一个指向指向对象的句柄
64位长度的long,double会占用2个局部变量空间的Slot,其它的只占用一个,局部变量表所要的内存空间在编期间完成分配,当进入一个方法时已经确定,在运行时不能改变局部变量表的大小。
VM Stack中的异常有StackOverFlow, OOM.
三、本地方法栈
与VM Stack相似,只是其为Native方法服务。
四、Java堆
是虚拟机内存管理的最大的一块,被所有线程共享,在虚拟机启动时创建。唯一的作用就是存放 对象的实例,所有的对象实例与数组都在堆上分配。
注意:随着JIT的发展与逃逸分析技术的成熟,栈上分配 ,标替换等优化技术也可能 使对象在VM stack上分配成为可能。
从内存回收的角度 ,目的是为了更好的回收内存:
现在收集器基本使用分代收集算法 ,所以 Java Heap可以分为 新生代,老年代。再细致可以分为,Eden, From Survivor, To Survivor.
从内存分配的角度,目的是为了更快的分配内存:
线程共享的Java Heap中可以分配出多个线程私有的分配缓冲区(Thread Local Allocation Buffer, TLAB)。
可以抛出异常为OOM.
五、方法区
线程共享 ,用于存放 已经被虚拟机加载的类信息,常量 ,静态变量,即时编译器编译后的代码等,
在HotSpot虚拟机中,常常称为永久代,本质上两者并不一样,只是HotSpot虚拟机中选择使用永久代的回收方法来实现方法区而已。现在看来,这并不是一个方法,因为容易出现OOM的问题(由于永久代的上限),在新的HotSpot中,可能会放弃这种做法 。
相对而言,回收在方法区还是很少发生的,其回收的主要目标是常量池的回收与类型的卸载,两者都比较复杂。
可以抛出的异常是OOM。
运行时常量池
方法区的一部分。Class文件中除了类的版本,字段,方法,接口等描述信息外,还有常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号的引用,将在类加载后进入方法区的运行时常量池中。
运行时常量池具备动态性,运行期间也可以将新的常量放入池中,这种特性使用多的就是String.intern()方法。
可以抛出的异常是OOM .
六、直接内存
直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,但是也常常被使用,也可能会有OOM出现。在NIO中有使用。