Jvm运行时的数据区分为共享的数据区和私有区,共享区分为方法区和堆;私有区分为虚拟机栈,本地方法栈和程序计数器。
程序计数器
- 作用是当前线程所执行的字节码的信号指示器,字节码工作时就是通过改变计数器的值来选取下一条所需执行的字节码指令。
- 是线程私有的,因为线程是由CPU轮流切换线程,分配CPU时间执行的。所以每一个线程都需要一个独立的程序计数器。当线程中运行的是java代码时,程序计数器记录地址,若线程中运行的是native时,程序计数器为空。
- 此内存区域是唯一个没有规定任何OOM(OutOfMemoryError)情况的区域。
虚拟机栈
-
生命周期与线程相同。
-
java方法执行的内存模型:虚拟机栈的栈元素是栈帧,当有一个方法被调用时,代表这个方法的栈帧入栈;当这个方法返回时,其栈帧出栈,后进先出(LIFO)栈。因此,虚拟机栈中栈帧的入栈顺序就是方法调用顺序,栈帧可以理解为一个方法的运行空间,用来存储局部变量表、操作数栈、动态链接、方法出口等信息。
-
通常把java内存区分为栈(Stack)内存和堆(Heap)内存。栈指的是虚拟机栈,也就是虚拟机栈中的局部变量表部分,局部内存表所需的内存空间在编译期间完成分配。
-
在java虚拟机规范中,对这个区域规定了两种异常情况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowEror异常;如果虚拟机栈可以动态扩展,当扩展时无法申请到足够的内存时,会抛出OutOfMemoryError异常。
本地方法栈
- 与虚拟机栈作用类似,区别是虚拟机栈为执行字节码服务,而本地方法栈为虚拟机使用到的native方法服务。
- 虚拟机规范中对本地方法栈中的方法使用的语言,使用方式与数据结构没有强制规定,所以虚拟机可以自由实现它,有一些虚拟机(如HotSpot)将Java虚拟机栈和本地方法栈合并实现。
Java堆
- java堆是java虚拟机管理的一块内存,在虚拟机启动时创建,被线程共享。
- 此内存区域的唯一目的是存放对象实例。存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令)
- 虚拟机垃圾收集器的主要区域。有时被称为“GC堆”。java虚拟机规范规定,java堆可以处于物理不连续的内存空间中,只要逻辑连续就可。(磁盘空间,链表)
方法区
- 又叫静态区,跟堆一样,被所有的线程共享。
- 方法区用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。方法区的垃圾回收目标主要是针对常量池的回收和对类型的卸载(相对类加载而言)。
- Hotspot虚拟机的设计团队把GC分代收集扩展至方法区,或者说使用永久代来实现方法区。
- java虚拟机规范对这个区域非常宽松,不需要连续的内存,可以选择固定大小,可扩展,还可以选择不实现垃圾收集。垃圾收集行为在这个区域相对而言很少出现,这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载。
运行时常量池
- 是方法区的一部分,class文件除了有类的版本,字段,方法,接口等描述信息外,还有一个信息是常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。
- 运行时常量池相对于class文件常量池的另外一个特征是具备动态性,运行期间也可以将常量放入池中,例如String类的intern()方法。
参考书籍:周志明《深入理解java虚拟机》