深入理解JVM-各个阶段的作用

JAVA程序运行的步骤

Java程序的运行的步骤一般如下

  1. 编写 myapp.java
  2. 编译 javac myapp.java
  3. 执行java myapp
  4. 执行命令后会启动JVM执行你的程序,第一步先将你的myapp.class加载到进来即ClassLoad,其中ClassLoad又分为五个步骤,加载,验证,准备,解析,初始化。将程序的Class文件以及静态变量加载到方法区。然后在堆内创建对象。
  5. 执行引擎通过main方法运行

JVM架构

整体的架构图如下。下面具体介绍每一步的具体作用
深入理解JVM-各个阶段的作用

class loader

我们所说的类加载过程即是指JVM虚拟机把.class文件中类信息加载进内存,并进行解析生成对应的class对象的过程。

举个通俗点的例子来说,JVM在执行某段代码时,遇到了class A, 然而此时内存中并没有class A的相关信息,于是JVM就会到相应的class文件中去寻找class A的类信息,并加载进内存中,这就是我们所说的类加载过程。

由此可见,JVM不是一开始就把所有的类都加载进内存中,而是只有第一次遇到某个需要运行的类时才会加载,且只加载一次
ClassLoad分为五个步骤,加载,验证,准备,解析,初始化
深入理解JVM-各个阶段的作用

load

加载阶段是整个类加载的第一步,需要完成三件事情

  1. 通过一个类的全名获取定义此类的二进制流
  2. 将这个二进制流所代表的的静态存储结构转化为方法区运行时数据
  3. 在内存中生成一个代表这个类的Class对象,作为方法区这个类的各种数据的入口
    我们可以从各个位置获取到二进制流,所以load阶段不仅局限于jar。还包括socket jsp proxy等,只要能获取到二进制流都可以。如果要实现自己的类加载方法,需要继承ClassLoader,重写loadClass 或者findClass方法。
    源码如下深入理解JVM-各个阶段的作用

基本上都是调用一些Native方法。
后面会细说类加载以及双亲委派模型
加载的目的,是把class字节码文件从各个来源通过类加载器装载入内存中。
这里有两个重点:
字节码来源。一般的加载来源包括从本地路径下编译生成的.class文件,从jar包中的.class文件,从远程网络,以及动态代理实时编译
类加载器。一般包括启动类加载器,扩展类加载器,应用类加载器,以及用户的自定义类加载器。

vertify

验证的目的是确保Class文件的字节流中包含的信息全部符合java虚拟机规范。验证阶段大致分为四个阶段的动作

  • 文件格式验证
    保证输入的字节流能够正确的解析,并存储与方法区之内,只有通过了这个验证字节流才能被允许存储在方法区,后面的验证直接在方法区中的存储上,不会再操作字节流了
  • 元数据验证
    主要是验证语义,如是否有父类,是否能被继承,是不是抽象类等。
  • 字节码验证
    验证方法体。保证方法能正确执行。
  • 符号引用验证
    确保resolve能够正确运行,resolve主要是将字符引用转换为直接引用。见下面
prepare

初始化一些类变量,不是实例变量。实例变量将会在对象实例化的时候随对象一起分配在java堆中。而且只是初始化默认值,即0 ,0.0, null不会赋值,以及一些常量值如final 修饰的。

resolve

将符号引用转换为直接引用。可以理解为将java代码内部的调用过程,转为JVM内部的指向。比如我在代码里写了个new Hello();那么即是符号引用,转为直接引用变为了这个Hello对象在内存中的位置。如果类未被加载,则先加载

initialize

这是类加载的最后阶段,这里所有的静态变量会被赋初始值, 并且静态块将被执行

类加载器

Runtime data areas

深入理解JVM-各个阶段的作用

execution engine

执行字节码,与本地方法交互,不同的平台有不同jdk,即不同的本地方法