JVM内存模型

来源:互联网 发布:常州java培训 编辑:程序博客网 时间:2024/06/08 08:02
**JVM内存模型

**
JVM把它所管理的内存划分为若干个不同的数据区域。如图:
如图

1:程序计数器(Program Counter Register)

  一块较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器。每个线程都有一个独立的程序计数器,各个线程之间的程序计数器不相互影响,独立存储。是一块线程私有的内存。如果当前线程正在执行Java方法,该计数器记录的是当前正在执行的虚拟机字节码的位置,如果当前线程正在执行native方法,该计数器为空。程序计数器也是JVM中唯一不会抛出OutOfMemoryError的区域。

2:JVM栈(Java Virtual Machine Stacks)

JVM栈也是线程私有的一块区域,生命周期与线程相同。程序每起一个线程,JVM就会给该线程分配一个JVM栈和程序计数器。JVM栈是Java方法执行的内存模型:每个方法都是JVM栈中的一个栈帧(Stack Frame)。

JVM栈中的局部变量表存储了编译器可知的基本数据类型:boolean、byte、char、short、int、float、long、double、reference(对象引用)和returnAddress(指向了一条字节码指令的地址)。其中64位长度的long和double类型的数据会占用两个局部变量空间(Slot),其余的只占用一个。局部变量表的大小在编译期就会确定,在运行时不会改变大小。

当请求的栈深度超过了jvm允许的深度会抛出StackOverflowError,栈的大小可以通过-Xss来调整如:-Xss128k。

3:本地方法栈(Native Method Stack)

与JVM栈的作用相似,只是Jvm栈用于执行java方法,本地方法栈用于执行native方法(java调用非java代码的接口)。

4:Java堆(Java Heap)

堆是JVM当中占用内存最大的一块。所有线程共享一块堆区域。在虚拟机启动时创建,用于存放对象实例。几乎所有的对象实例都在这里分配内存。堆也是垃圾回收的主要区域。按照当前最主流的分代收集算法来看:可以分为新生代和老年代。从内存分配的角度来看,Java堆还可以划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer,TLAB)。堆的大小可以通过-Xmx和-Xms来调整。当堆中没有足够的内存来分配对象实例,并内存无法扩展时,抛出OutOfMemoryError。

5:方法区(Method Area)

同样是线程共享的一块区域,在虚拟机规范把方法区描述成了堆的一个逻辑部分,但它还有一个名字叫非堆(Non-Heap)。在垃圾回收时也习惯称之为永久代。
永久代用于存放虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。在JDK1.7的hotspot中,方法区中的字符串常量池被移动到了堆当中。

在JDK1.8中,方法区被替换为了元空间(Meta Space),作用大致不变,不同的是元空间使用的是本地的内存,这意味着在JDK1.8中不再需要调整方法区的大小,参数-XX:PermSize和-XX:MaxPermSize也不复存在。类元数据分配受到可用的本机内存容量的限制。参数-XX:MaxMetaspaceSize用于调整元空间的大小。

6:运行时常量池

有三个概念需要清楚:

常量池(Constant Pool):常量池数据编译期被确定,是Class文件中的一部分。存储了类、方法、接口等中的常量,当然也包括字符串常量。

字符串池/字符串常量池(String Pool/String Constant Pool):是常量池中的一部分,存储编译期类中产生的字符串类型数据。

运行时常量池(Runtime Constant Pool):JDK1.8之后加入了堆,所有线程共享。虚拟机加载Class后把常量池中的数据放入到运行时常量池。

运行时常量池在JDK1.7被移动到了堆区域。一般用于存放运行时生成的符号引用。

才疏学浅,若有错误请指正。

原创粉丝点击