JVM内存模型
jvm=类加载器(classLoader) + 执行引擎(execution engine) + 运行时数据区(runtime data area)
一、运行时数据区
jvm在执行Java程序的时候会把它所管理的内存划分为若干个不同的数据区域,这些区域有着各自的用途。
jvm运行时数据区分为:
- 程序计数器
- Java堆
- java虚拟机栈
- 本地方法栈
- 方法区
二、程序计数器
- 线程私有,它的生命周期与线程相同。
- 可以看作当前线程所执行的字节码行号指示器。
- 程序计数器中存储的数据所占空间大小不会随着程序的执行而发生改变,所以此区域不会出现OOM(out of memory)内存溢出。
三、java虚拟机栈
线程私有,它的生命周期与线程相同。
虚拟机栈描述的是java方法执行的内存模型,每个方法被执行的时候会创建一个栈帧,用于存放局部变量表、操作数栈、动态链接、方法出口等信息。每个方法被调用直至执行完成的过程中,就对应着一个栈帧在虚拟栈中从入栈到出栈的过程。
局部变量表存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、double、long)、对象引用(reference类型)。局部变量表所需的内存空间是在编译期间完成分配的,当进入一个方法时,这个方法需要在栈中分配多大的空间时已经确定的,在方法运行期间不会改变局部变量表的大小。
操作数栈和局部变量表一样,在编译时期已经确定了该方法所需的最大空间。当一个方法开始执行的时候,这个方法的操作数栈是空的,在方法执行的过程中,会有各种字节码指令往操作数栈中写入和提取内容,也就是出栈、入栈操作(例如,在算术运算的时候是通过操作数栈来进行的)。
动态链接,每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,Class文件的常量池中存有大量的符号引用,字节码中的方法调用指令就以常量池中方法的符号引用为参数。这些符号引用一部分会在类加载阶段或者第一次使用的时候就转化为直接引用(静态方法,私有方法等),这种转化称为静态解析,另一部分将在每一次运行期间转化为直接引用,这部分称为动态连接
四、本地方法栈
本地方法栈是为jvm虚拟机用到的native方法服务
五、java堆
- java堆是被所有线程共享的,用来存放实例对象和数组,几乎所有实例对象都是在堆中分配内存。
- java堆是垃圾收集器管理的主要区域,从内存回收的角度看,由于现在垃圾收集器大多采用分代收集算法,所以java堆又分为:新生代和老年代(1:2),新生代又分为Eden区,From Survivor区、To Survivor区(8:1:1)
- Java堆可以动态扩展,如果超过了阈值则会内存溢出
六、方法区
- 方法区用于已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
- 方法区也可以动态扩展,如果超过了阈值则会内存溢出
运行时常量池
- 运行时常量池也是方法区的一部分
- Class文件中的常量池(编译器生成的各种字面量和符号引用)会在类加载后被放入运行时常量池中
- 除了在编译期间生成的常量,还允许动态生成, 例如 String 类的 intern()。这部分常量也会被放入运行时常量池。
