java中内存模型划分及Runtime Data Area(运行时数据区)
在Java中我们常常说到的内存管理就是针对运行时数据区进行管理(如何分配和回收内存空间)。
VM中的运行时数据区应该包括(1,2,3,4,5)。在JVM规范中虽然规定了程序在执行期间运行时数据区应该包括这几部分,
但是至于具体如何实现并没有做出规定,不同的虚拟机厂商可以有不同的实现方式。
1、程序计数器(Program Counter Register)
其他名称:PC寄存器
是否共享:线程私有:
用途: 指示执行那条指指令
注意:
1、执行非native方法,程序计数器保存的是当前需要执行指令的地址。执行native方式,程序技术器的值是undefined
2、由于程序计数器中存储的数据所占的空间大小"不会"随程序的执行而发生变化,因此程序计数器不会发生内存溢出(outOfmemory)的现象
示例:
源码:
package memoryModel;
/**
* @Auther: 魂牵
* @Date: 2019/6/28 0028 13:53
* @Description:
*/
public class TestProGramCounter {
public int test() {
int a = 1;
int b = 8;
int e = 2;
int c = 7;
int d = 5;
return (a + b + e) * c / d;
}
}
编译后:
D:\ideaproject\idea-spark-2-25\JVMDemoJDK1.8\src\main\java\memoryModel>javap -c TestProGramCounter.class
Compiled from "TestProGramCounter.java"
public class memoryModel.TestProGramCounter {
public memoryModel.TestProGramCounter();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public int test();
Code:
0: iconst_1
1: istore_1
2: bipush 8
4: istore_2
5: iconst_2
6: istore_3
7: bipush 7
9: istore 4
11: iconst_5
12: istore 5
14: iload_1
15: iload_2
16: iadd
17: iload_3
18: iadd
19: iload 4
21: imul
22: iload 5
24: idiv
25: ireturn
备注:code 下方的数字就是类似的程序技术器一样的功能。
2、虚拟机栈(java栈)(VM Stack)
其他名称:java栈
是否共享:线程私有
内存要求:不需要保证是连续的
启动时间:方法被调用时创建
用途: 栈内存为执行java方法服务,方法被调用时(线程被执行时)创建栈帧———>局部变量表————>局部变量,对象引用
注意: 1、栈内存大小:java虚拟机规范即允许java虚拟机栈固定大小,也允许根据计算动态来扩展和收缩。
2、异常:1>(创建时)若线程请求分配的栈容量超过java虚拟机栈允许的最大容量,java虚拟机就会抛出StackOverflowError。
2>(运行时)若栈内存动态扩展,请求的扩展的时候无法申请到足够的内存,或者在创建线程没有足够的内存来创建对应的
虚拟机栈,java虚拟机就会抛出outofmemory的异常
3、每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程
栈帧:存储数据和部分过程结果的数据结构,同时也被用来处理动态链接(Dynamic Linking)、方法返回值和异常分派(Dispatch Exception)
生命周期:随着方法调用而创建,随着方法结束而销毁。
3、本地方法栈(Native Method Stack)
是否共享:不共享
内存要求:可固定,也可扩展
启动时间:如果支持本地方法栈,在线程创建时按线程分配
用途:其他语言实现的指令集解释器(及方法或接口)。
注意:
1、java虚拟机不支持传统栈(及:C stacks)的话,则无需支持本地方法栈,若支持,那这个栈一般在线程创建的时候按线程分配
2、可固定,也可扩展 (会抛异常:StackOverflowError(容量不够)或outofMemory(扩容时))
3、Sun HotSPot把虚拟机栈和本地方法栈和二为一
4、java堆,及内存堆(Heap)
是否共享:线程共享
内存要求:不需要保证是连续的(物理上不连续的,逻辑上连续的)
启动时间:JVM启动时创建
用途:存储被GC所管理的各种对象(这些对象无需,也无法显示的被销毁)
注意:实际所需的堆超过自动内存管理系统(及GC)能提供的最大容量,跑抛异常outofmemory
5、方法区(Method Area)
是否共享:线程共享
内存要求:实际内存可以不连续
启动时间:JVM启动时创建
用途:存储每一个类的结构信息 eg:运行时常量池、字段和方法数据、构造函数和普通方法的字节码内容、还包括一些类、实例、接口、初始化时的特殊方法
注意:
1、虽然方法区是堆的逻辑组成部分,但是简单的虚拟机实现可以选择在这个区域不实现GC
2、方法区容量可固定也可随着程序执行的需求动态扩展。
3、Sun HotSpot虚拟机将方法区叫做永久代(permanent Generation)
4、在Java 8里已被废除了,被元空间取代;
6、运行时常量池(Runtime Constant Pool)
首要:方法区的一部分
用途:存储类或接口的常量池,包含若干种不同的常量:从编译器可知的数值字面量到必须运行解析后才能获得的方法或字段引用。
注意:
1、运行时常量池扮演类似传统语言中的符号表的角色,不过它的存储范围比通常意义的符号表要更为广泛
2、每个运行时常量池都分配在java虚拟机的方法区中,在类和接口被加载到虚拟机后,对应的运行时常量池也被创建出来。
3、创建常量池时,所需内存超过方法区所能提供的最大容量时,抛异常outofmemory
备注:本文使用JDK7编写
书籍:Java虚拟机规范 (Java SE 7版)