JVM内存模型

xiaoxiao2021-02-27  300

前面的文章中讲到,JVM内存结构中每个区域都担当着不同的角色。弄清楚这些区域的功能和各自的生命周期对java语言的使用是很有必要的。

一 程序计数器

程序计数器(Program Counter Register),它是java虚拟机规范中唯一一个没有规定任何OutOfMemoryError情况的区域。每一个线程都有一个独立属于自己的程序计数器,它是线程私有的,所以它是随着线程的创建而创建。当线程执行一个java方法时,计数器记录的是当前正在执行的虚拟机字节码指令的地址,指向下一条要执行的指令地址;当执行的是一个本地方法(Native Method)时,这个计数器的值为空(Undefined)。

二 方法区

方法区(Method Area)是各个线程共享的内存区域,用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。通常将该区域与永久区关联在一起(这两者并不等价,这是针对HotSpot虚拟机的,其他的如BEA JRockit、IBM J9等虚拟机来说则没有永久区的概念)。方法区可以不需要连续的内存并且可以选择固定大小或者可扩展,还可以选择不实现垃圾收集。这个区域的内存回收的目标主要是针对常量池(JDK 1.7以后,存放String等常量信息的常量池已经从这里移除了)的回收和对类卸载。

三 java 堆

java 堆(java Heap)可以说是与程序的开发是最密切相关的。它是java虚拟机所管理的内存中最大的一块。是被所有线程共享的一块区域,在虚拟机启动时创建。该区域的唯一目的就是存放对象实例,几乎所有的对象实例和数组都在这里分配内存。当然这并不是绝对。java堆是垃圾回收器管理的主要区域。因为现在的内存管理基本上都是采用的分代收集算法,因此java堆可以细分为:新生代和老年代。 新生代还分为Eden区、From区和To区。Eden是伊甸园的意思,是所有对象实例产生的地方。From区和To区是两块大小相同的区域,使用的垃圾回收机制是复制算法。具体关于垃圾回收机制的内容会在后面的文章中整理。 java堆是可以处在不连续的内存空间中,只要保证逻辑上是连续的。可以固定大小也可以是可扩展的(通过-Xms 和 -Xmx 参数控制)。

四 java 栈

与PC寄存器一样,java栈(java Vritual Machine Stacks)也是线程私有的,由一系列的帧组成,所以又称帧栈。java栈描述的是java方法执行的内存模型:每个方法在执行时都会创建一个栈帧用于存放局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在栈中入栈到出栈的过程。可以看出,java栈并不需要垃圾回收,因为当一个方法执行完成后,栈中的数据已自动清空。

1 局部变量表

局部变量表 存储的不仅仅是局部变量,而是包括参数和局部变量。 如上图,当执行第一个静态方法时,对应的右面的第一个栈帧入栈,并且每一列中保存着对应的参数与局部变量。而下面的实例方法则有些不同,这里的第一列中存储的是执行当前方法的实例引用。

2 操作数栈

java中的所有参数传递使用的都是操作数栈。我们以一个简单的加法程序为例: 以a=100, b=98为例。右下角是反编译后代码的一部分。 0: iconst_0 // 压栈 a、b、c压入局部变量表中(图中开始之前应该c = 0没有画出来) 1: istore_2 // 弹出int,存放于局部变量2 2: iload_0 // 把局部变量0压栈,即把a压入操作数栈中 3: iload_1 // 局部变量1压栈,即把b压入操作数栈中 4: iadd //弹出2个变量,求和,将结果压入操作数栈中 5: istore_2 //弹出结果,放于局部变量2 6: iload_2 //局部变量2压入局部变量中 7: ireturn //返回

五 堆、栈、方法区交互

public class AppMain //运行时, jvm 把appmain的信息都放入方法区 { public static void main(String[] args) { Sample test1 = new Sample( " 测试1 " ); //test1是引用,所以放到栈区里, Sample是自定义对象应该放到堆里面 Sample test2 = new Sample( " 测试2 " ); test1.printName(); test2.printName(); } public class Sample //运行时, jvm 把Sample的信息都放入方法区 { private name; //new Sample实例后, name 引用放入栈区里, name 对象放入堆里 public Sample(String name) { this .name = name; } //print方法本身放入方法区里。 public void printName() { System.out.println(name); } }
转载请注明原文地址: https://www.6miu.com/read-3540.html

最新回复(0)