要理解JVM的运行时数据区,必须先要理解JVM的体系结构,因为虚拟机的体系结构基本上解释了“为什么会有这些运行时数据区”。JVM的体系结构如下:
由此可见,运行时数据区的划分,是和JVM的体系结构相关的。本文主要介绍运行时数据区的划分,对体系结构不做深入的。简单概括一下,类加载器子系统用于将class文件加载到虚拟机的运行时数据区中(准确的说应该是方法区)。可以认为执行引擎是字节码的执行机制,一个线程可以看做是一个执行引擎的实例。下面介绍运行时数据区:
在字面意思上,“方法区”这个词会让人产生。因为方法区存放的不只是方法,它存放的是类型信息。我们在写程序的时候,几乎总是在和类,对象打交道,我们知道根据一个类可以创建对象。一般来说,我们的是对象,访问对象的属性,调用对象的方法等,但是我们要思考这样一个问题,虚拟机根据什么信息知道如何创建对象的呢?当然是根据这个对象的类型信息,但是这个类型信息在哪里呢?现在我们知道是在方法区中。那么类型信息是被谁加载到方法区中的呢?由的体系结构图,我们可以知道是类加载器子系统?那么所谓的类型信息,都包含什么信息呢?这些信息又是如何存放的呢?这里的类型信息,可以笼统的认为就是我们前面过的一个class文件,类加载器子系统将会提取class文件里面的类型信息,并将这些类型信息存放到方法区中。至于方法区中如何存放一个类型数据,是和JVM的具体实现相关的。但是不管如何实现,一个类的类型信息总是会包含如下信息:
如果对class文件格式比较熟悉的话,可以看出,这些信息都是在class文件中描述过的。由于我们无法看到类型信息具体是如何存储的,但是大致可以将类型信息看做一个class文件,这有助于我们的理解。下面再次列出class文件结构的表格,读者可以对比class文件中的内容到类型数据上,该表中的各种数据已经在前面的博客中详细过:
每个class都是被一个类加载器加载到方法区的,类型信息中的到类的ClassLoader对象的引用,表明了当前的类是被哪个类加载器加载的,这个信息同时也标示了当前的类型的名称空间。
每当一个class文件被成功的加载到方法区中,JVM总会创建一个Class对象,来唯一标示这个类。这个Class对象可以看做是类加载过程的产物,由于它描述了整个类型信息,而Java中的反射也是针对的类型信息,所以这个Class对象是反射的基石,大多数反射API都是根据Class对象来实现的。
而静态变量也是存在于类型信息中,可以这么说,类型信息中,会有专门的区域存放类的静态变量。与存在于对象中的实例变量不同,静态变量存在于类型数据中,每个类型只有一份,所以也叫类变量。
方法区是一个相对来说比较固定的内存区,因为它存放的是类型信息,而类型信息在被加载到方法区中之后,除了必要的连接和初始化,一般不会有较大改动,一般情况下,JVM也不会卸载类型信息,所以方法区也可以称为JVM的静态区。一个类型的生命周期一般就是整个程序的生命周期。这也是为什么要慎用静态变量的原因所在,因为静态变量随类型信息存放在方法区中,生命周期很长,如果使用不当,很容易造成内存泄露。一个JVM实例中只存在一个方法区,方法区中的所有类型数据被所程共享。
方法区是存放类型数据的,而堆则是存放运行时产生的对象的。和C++不同的是,Java只能在堆中存放对象,而不能在栈上分配对象,所有运行时产生的对象全部都存放于堆中,包括数组。我们知道,在Java中,数组也是对象。一个JVM实例中只有一个堆,所程共享堆中的数据(对象)。
Java虚拟机支持几种不同的创建对象的指令,如new,anewarray等。这些指令执行的结果就是在堆中分配内存,并创建对象。但是Java虚拟机的指令集中并不包含任何内存的指令,因而我们也就不能手动内存。所有被创建的对象都会被一个叫做垃圾收集器(GC)的模块自动回收,垃圾收集器有不同的实现方式,他们以特定的方式判断对象是否过期,并以特定的方式对对象进行回收,关于垃圾收集的话题不是本文的重点,这里就不多说了。我们只要知道:所有创建的对象都存在堆中,而垃圾收集器会自动回收过期的对象,所以,JVM的堆区是垃圾收集器的“重点管理区”。
Java栈是一个线程的执行区域,它保存着一个线程中的方法的调用状态,也可以说,一个Java线程的运行状态,都由一个Java栈来保存。在这个栈中,每一方法对应一个栈帧,请注意区分栈帧和栈这两个概念。栈指的是整个线程的执行栈,栈帧是栈中的一个单位,每个方法对应一个栈帧。JVM会对Java栈执行两种操作:压栈和出栈。这两种操作在执行时都是以帧(栈帧)为单位的。当调用了一个新的方法,就会压入一个栈帧,当一个方法调用完成,就会弹出这个方法的栈帧,回到调用者的栈帧。
举例来说,如果方法a调用了方法b,而方法b中调用了方法c。这个过程中的方法调用和返回的装状态是这样的(其中图中两条虚线之间表示Java栈,每个方块表示一个特定方法的栈帧)
Java栈上的所有数据都是线程私有的,也就是说,每个线程都会有自己的Java栈,不会相互访问其他Java栈中的数据。
pc寄存器用于存放一条指令的地址,这条指令就是虚拟机要执行的下一条指令。pc寄存器和线程相关联,每一个线程都有一个PC寄存器。
我们知道Java可以和C/C++互调。如果当前线程执行的代码是C/C++写的本地代码,那么这些方法就在本地方法栈中执行,而不会在Java栈中执行,Java栈中只执行Java方法。返回搜狐,查看更多
纹身的忌讳和讲究