JVM类加载的流程

类加载器(ClassLoader)

在java代码中,类型(Integer)的加载、连接、初始化都是在程序的运行期间完成的,提高了代码的灵活性。实现厂商可以自已实现一些功能 ,但是要和jvm的规范一致,提高了更多可能性

类加载器的剖析

每一个类型(java.lang.String)都是通过类加载器将类放入内存中。

java虚拟机结束进程:

  1. 执行了System.exit()
  2. 程序下常执行结束
  3. 程序在执行过程中遇到了异常或错误异常终止
  4. 由于操作系统出现了错误而导致虚拟机进程终止

每个阶段的流程

  • 加载:将已经在硬盘上或者是通过其它媒阶传来的字节码文件加载到内存中
  • 连接:(连接这里分成三个阶段)

    • 验证:验证字节码文件的正确性,防止被一些程序修改
    • 准备:为类里面的静态变量分配内存,并给这些变量默认值(比如:int的默认值是0)
    • 解析:将常量池里的符号引用替换成直接引用
  • 初始化:给变量赋值正确的初始值

Java程序对类的使用方式可分为两种

主动使用

  • 创建类的实例(比如:new String)主动的去new一个对象
  • 访问某个类或者接口静态变量,或者对该静态变量赋值(给静态变量取值或者是在静态变量赋值)
  • 调用类的静态方法
  • 反射 通过使用反射,将全限定类名传入创建出对应的对象
  • 初始化一个类的子类(父类也会使用)
  • java虚拟机启动时被标明为启动类的类(比如包含了main方法的类)
  • JDK1.7开始提供的动态语言支持:java.lang.invoke.MethodHandle实例的解析结果REF_getStatic,REF_putStatic,PEF_invokeStatic句柄对应的类没有初始化,则初始化

所有的java虚拟机实现必须在每个类或接口被java程序“首次主动使用”时,才初始化它们

助记符:(第一条和第二条使用的)

  • getstatic 获取静态变量的值
  • putstatic 给静态变量赋值
  • invokestatic 调用静态方法

其它的几种情况(被动使用)不会导致类的初始化,并不意味着不会加载种连接

类的加载

类的加载是指将类的.class文件中的二进制数据读入到内存中。将其放到运行时数据区的方法区内,然后在内存中创建一个java.lang.Class对象(规范并未说明Class对象位于那里,HotSpot虚拟机将其放在了方法区中)用来封装类的方法区内的数据结构

加载class的方式

  • 从本地系统中直接加载
  • 通过网络下载class文件
  • 从zip,jar等归档文件中加载class文件
  • 从专有的数据库中提取class文件
  • 将java源文件动态编译成class文件

代码测试

public class CodeTest {
public static void main(String[] args) {
    System.out.println(Child.str);
}
}
class Parent{
    public static String str = "Hello World";
    static {
        System.out.println("这个是一个父类");
    }
}
class Child extends Parent{
    public static String str2 ="Hello World2";
    static {
        System.out.println("这个是一个子类");
    }
}

通过执行上面的代码,会发现输出的内容:

这个是一个父类
Hello World

这里是通过子类调用的父类中的静态变量,这里就是我上面说过的调用类的静态方法时主动的使用这个类,这里只是使用了Child的名字,并没有主动的使用Child

当代码修改成以下的内容的时,这里也只是修改了输出的静态变量

public class CodeTest {
public static void main(String[] args) {
    System.out.println(Child.str2);
}
}

class Parent{
    public static String str = "Hello World";
    static {
        System.out.println("这个是一个父类");
    }
}

class Child extends Parent{
    public static String str2 ="Hello World2";
    static {
        System.out.println("这个是一个子类");
    }
}

通过执行上面的代码,会发现输出的内容:

这个是一个父类
这个是一个子类
Hello World2

这里只是单单的修改了使用的静态变量,为什么会出现这种情况?
上面说到初始化一个类的子类(父类也会使用)如果要主动使用一个类的子类,所使用的这个类一定会初始化,对于上面的要求,使用一个子类,它的所有的父类一定会先初始化,所以Parent会先初始化,就导致了先执行Parent的静态代码块,初始化完成之后回到Child类,Child类在进行初始化,执行这个类的静态代码块,初始化完成后使用这类的str2

Last modification:October 19th, 2019 at 10:38 am
如果觉得我的文章对你有用,请随意赞赏

Leave a Comment