JVM 结构学习(图文解释)

来源:互联网 发布:腾讯云和阿里云学生 编辑:程序博客网 时间:2024/04/29 12:43

bu最近看了《深入理解java虚拟机》的几章,也翻了翻大牛们的博客,在这里把知识梳理一下,方便回顾。有错误的地方,希望大家指出来。

简单的从Java栈、PC寄存器、堆、方法区、垃圾回收系统等组件来了解一个程序从运行到结束的过程中虚拟机是如何变化的。这些组件具体用处我将用下面的实例带大家过一遍。

/** * Created by jintx on 2017/7/21. */public class Test {    public static  void main(String args[]){//start        Test test1 = new Test();        Test test2 = new Test();        int sum = test1.add(1,2);        int sub = test2.sub(2,1);        String str1 = new String("abc");        String str2 = new String("abc");        doSome();    }    public static  void doSome(){        System.out.print("static method");    }    public int add(int x, int y){        return x + y ;    }    public int sub(int x ,int y){        return x - y ;    }}

我们先来看在运行start行虚拟机内部发生了什么。

Step1
main函数执行的线程称为主线程,当产生一个线程时会在java栈产生与之对应的栈帧链,java栈存放着一个一个java帧,每当调用方法时,就会在java栈中压进一个java帧。

每一个帧中包含着局部变量表、操作数栈、动态链接、附加信息。其中局部变量表存放着方法参数和局部变量。操作数栈(Operand Stack)也常称为操作栈,是一个后入先出栈。方法执行中进行算术运算或者是调用其他的方法进行参数传递的时候是通过操作数栈进行的。

与java线程产生的还有对应的一个PC寄存器。简单的理解PC寄存器的内容总是下一条将被执行的指令“地址”。这里的地址可以是指针,或是起始指令的偏移量。
所以在虚拟机内部的变化是:

这里写图片描述

若有多个线程,那么对应的情况如下。

这里写图片描述

Step2
在看回程序Test test1 = new Test(); Test test2 = new Test();,这里定义了两个Test对象。虚拟机内部对应的变化是:

这里写图片描述

可以看到在java栈中栈帧1放了test1和test2对象的引用(不是对象!!),而真正的对象则被存放在了堆中。而方法区就储被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,当调用对象中的方法时,就会索引到方法区找到想对应的方法。

Step3
接着下面两个方法就被调用了 int sum = test1.add(1,2); int sub =test2.sub(2,1); 首先两个方法的调用会在主线程中压入对应的两个帧。方法的具体信息,栈帧1会先通过对象的引用找到堆中的对象,再通过对象存放着方法区的引用找到方法区中实现方式。

这里写图片描述

对应着的java栈中的帧一多了sum和sub,因为int是基本数据类型,所以sum和sub的引用和值都会被存储在帧中的局部变量中。PC寄存器也指向了栈帧三,表示此刻执行的sub()函数。

Step4
当执行到 String str1 = new String(“abc”);String str2 = new String(“abc”);的时候sub()、和add()方法已经执行完毕。对应的帧就会被执行出栈操作。
str1和str2对象的引用就会在栈帧一,对象出现在堆,String的类信息在方法区。

这里写图片描述

注意的是在栈帧一还出现了3和1,这是因为执行了sub、add方法后等到的结果,因为sum和sub是基本数据类型,所以在帧不仅保留了sub,add的引用还中保留了3和1的值。

而在方法区中的常量池出现了”abc”,这是因为”abc”是一个常量,而方法区中的常量池不仅保存着常量还存放静态变量、静态方法。所以”abc”不会出现在帧中。

当执行String str1 = new String(“abc”)时,在常量池生产常量”abc”,但在之后的new String(“abc”),会想检测常量池是否存在常量”abc”,若存在就不会再生成。所以只有一个常量在常量池。

Step5
当执行doSome();,因为doSome()是一个静态方法,所以会在常量池中出现,这也就是为什么我们可以不指定对象就可以用对象中的方法。看到这里我想大家都明白了为什么堆和方法区中的数据是可以共享了,而帧中局部变量是不能共享的吧。

这里写图片描述

出错的地方希望大侠们能给指出,谢谢。

原创粉丝点击