JVM-体系结构

大家都知道,java号称一次编译,处处运行。之所以能够这么强大,jvm功不可没。直接上图:

笔者看过这张图很多次,一直没能理解。一直到最近,觉得能够明白到写博客了。一个java文件通过编译可以得到class文件,class文件就是java虚拟机能够执行的二进制文件。而程序在运行的时候,需要在内存中有数据空间。在jvm中,这个数据空间叫做运行时数据区。运行时数据区,一共有5类空间,分别是:方法区,堆区,java栈,PC寄存器(我更喜欢叫程序计数器,jvm中没有寄存器)以及本地方法栈。
当jvm运行的时候,会先把class文件以二进制数据流的形式加载到方法区,这个过程通过类加载器系统完成(之所以叫做类加载器系统,是因为类加载器可以不只有一个,但常用的是启动类加载器)。一个个说吧!
方法区存储的是类的元数据,包括类的名称,超类等等元数据信息。这些信息中有3个比较重要的:
1 类的常量池
2 一个到当前类的Class类对象的引用
3 一个到当前类的ClassLoader类对象的引用

对于第二个,我想用过java反射机制的都应该明白了他的设计。在java反射机制中,通过Class.forName()等几种方法可以得到特定类的对象。对于第三个,用于记录装载当前类的类装载器对象。对于第一个,这里想重点说下: jvm为每一个被装载的类都维护一个常量池,这个常量池包含了直接常量和对其他类型,字段和方法的符号引用。其中,直接常量很好理解,比如String str="hello";那么str就是一个直接常量。什么是符号引用呢?比如MyClass mc=new MyClass();当类装载器装载这条语句所在类的时候,就会在常量池中记录MyClass xxx.xxx.MyClass,这个就叫做类型的符号引用,当然还有字段的符号引用,方法的符号引用等等。因为常量池中包含了当前类所用到的所有的符号引用(符号引用之后会变成直接引用),所以他在java程序的动态连接中有很重要的位置。
有些时候,为了提高jvm的性能,可能方法区会包含除了上述数据以外的其他数据,比如方法表:jvm对每个装载的非抽象类,都生成一个方法表,把他作为类信息的一部分存储在方法区。这样,以后再查找类方法时就不需要读取类信息,而是根据方法表直接访问。
当类装载器将class文件装载到了方法区,执行引擎就开启从指定类的public static void main()方法执行了,执行过程中会产生对象实例, 这些对象实例存储在堆区。我们知道,在类的对象中,对象的数据是属于对象的,但对象的方法是调用类的方法,因此堆区中的类对象需要“记得”自己在方法区的类方法。因此,对于堆区的设计,可以是下面几种方案:







上面的几种方案就不具体说了,很容易理解。对于堆区,还有个很重要的事情,就是垃圾回收,我们知道垃圾回收是java的一大特色,垃圾回收主要的作用空间就是堆区。关于垃圾回收,以后再说。关于堆区和方法区,下面举个例子:
对于下面的代码,我们来看他的执行过程。
public class A{
    private int speed;
    public void set(){
        speed=4;
    }
} 
public class B{
    public static void main(String [] args){
        A a=new A();
        a.set();
    }
}

当运行java B的时候,执行过程如下:首先类装载器加载类B到方法区(其中在常量池中有A的符号引用),接着执行第一行命令,看到使用new关键字,执行引擎需要在堆区新建一个A的对象空间。执行引擎查看B的常量池,发现对A存储的只是一个符号引用,不是直接引用。因此可能需要装载类A到方法区,装载完毕后,返回一个方法区类A的指针引用给类B的常量池(以后类B的常量池可以直接通过引用找到类A)。之后,由于类B知道了类A的详细信息,执行引擎就可以在堆区分配一个类A的实例对象,并将改对象的引用压如到当前线程的java栈中。第一条命令执行结束。
接着说其他三个区域。java栈,程序计数器,本地方法栈(不一定每个线程都有)对应一个线程。就是说,jvm每有一个线程被创建,就会为这个线程对应一个java栈和程序计数器(本地方法栈看是否需要)。由于java栈是线程独有,因此不存在同步问题。与此相对,方法区和堆区都是所有线程共享的区域(从代码角度很容易理解),因此存在线程安全问题。下图能够说明这一点:

java栈是线程执行过程中存放局部变量,操作数等信息的空间他的基本单位是帧。java栈对应的线程每执行一个方法,就会向栈中压如一个栈帧。这个栈帧包括局部变量区,操作数栈和帧数据区。
PC寄存器实际上是程序计数器,用来记录当前线程要执行的下一条指令是什么。
当一个线程在执行过程中需要调用本地方法时,就会创建一个本地方法栈功本地方法使用。

至此,大概介绍了下jvm的基本结构以及他的运行时数据区的基本功能。

猜你喜欢

转载自yizhenn.iteye.com/blog/2290127