JVM学习之Java类加载

Java类加载

我们知道,java源代码(*.java)不能直接执行,我们需要通过java编译器javac.exe将源代码编译为java字解码文件(*.class),然后再通过java.exe在JVM中解释此程序。

一、Java程序执行流程
这里写图片描述
二、类加载器(ClassLoader)
这里写图片描述
类加载器有以下几种类型:

  • 1、启动类加载器(Bootstrap ClassLoader):该ClassLoader是jvm在启动时创建的,用于加载 $JAVA_HOME/jre/lib下面的类库(或者通过参数-Xbootclasspath指定)。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不能直接通过引用进行操作。

  • 2、扩展类加载器(ExtClassLoader):该ClassLoader是在sun.misc.Launcher里作为一个内部类ExtClassLoader定义的(即 sun.misc.Launcher.ExtClassLoader),ExtClassLoader会加载 $JAVA_HOME/jre/lib/ext下的类库(或者通过参数-Djava.ext.dirs指定)。

  • 3、 应用程序类加载器(AppClassLoader):应用程序类加载器,该ClassLoader同样是在sun.misc.Launcher里作为一个内部类AppClassLoader定义的(即 sun.misc.Launcher.AppClassLoader),AppClassLoader会加载java环境变量CLASSPATH所指定的路径下的类库,而CLASSPATH所指定的路径可以通过System.getProperty(“java.class.path”)获取;当然,该变量也可以覆盖,可以使用参数-cp,例如:java -cp 路径 (可以指定要执行的class目录)。

  • 4、自定义类加载器(CustomClassLoader):该ClassLoader是指我们自定义的ClassLoader,比如tomcat的StandardClassLoader属于这一类;当然,大部分情况下使用AppClassLoader就足够了。

三、双亲委派加载机制

  • 1、类加载源码:
protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }
  • 2、加载过程:
    • 1)、首先验证这个类是否已经加载过,如果加载过直接返回。
    • 2)、如果没有加载过,再去判断是否有父加载器。如果有就递归调用父加载器验证是否加载此类。
    • 3)、如果没有父类加载器则调用启动类加载器(Bootstrap ClassLoader)加载此类。
    • 4)、如果启动类加载器(Bootstrap ClassLoader)也未找到此类,则会调用findClass(name)进行加载。
    • 5)、如果findClass(name)还未找到就抛ClassNotFoundException异常。

四、双亲委派加载机制好处

  • 1、避免类被重复加载
  • 2、安全:假如自定义一个Java中已经有的类,初始化的时候JVM会先委托BootStrapClassLoader加载JDK中已有的类,而自定义的类永远都不会被加载。

五、类加载详细过程
这里写图片描述

  • 1、加载:类加载阶段就是由类加载器负责根据一个类的全限定名来读取此类的二进制字节流到JVM内部,并存储在运行时内存区的方法区,然后将其转换为一个与目标类型对应的java.lang.Class对象实例(Java虚拟机规范并没有明确要求一定要存储在堆区中,只是hotspot选择将Class对象存储在方法区中),这个Class对象在日后就会作为方法区中该类的各种数据的访问入口。
  • 2、连接:是将加载到JVM中的二进制字节流的类数据信息合并到JVM的运行时状态中,经由验证、准备、解析三个阶段。
    • 验证:验证类数据信息是否符合JVM规范,是否是一个有效的字节码文件,验证内容涵盖了类数据信息的格式验证、语义分析、操作验证等
    • 准备:为类中的所有静态变量分配内存空间,并为其设置一个初始值(由于还没有产生对象,实例变量将不再此操作范围内)。
    • 解析:将常量池中所有的符号引用转为直接引用(得到类或者字段、方法在内存中的指针或者偏移量,以便直接调用该方法)。这个阶段可以在初始化之后再执行。
  • 3、初始化:对类内存数据进行初始化赋值等。
  • 4、使用:…
  • 5、卸载:类使用完成之后,根据其情况进行GC操作。

六、类初始化顺序

Foo.java

public class Foo {

    public Foo() {
        System.out.println("Foo.constructor...");
    }

    static {
        System.out.println("Foo.static...");
    }

    {
        System.out.println("Foo...");
    }
}

Son.java

public class Son extends Foo {

    public Son() {
        System.out.println("Son.constructor...");
    }

    static {
        System.out.println("Son.static...");
    }

    {
        System.out.println("Son...");
    }

    public static void main(String[] args) {
        new Son();
    }
}

控制台

Foo.static...
Son.static...
Foo...
Foo.constructor...
Son...
Son.constructor...

初始化说明:

  • 1、父类静态资源
  • 2、子类静态资源
  • 3、父类非静态资源
  • 4、父类构造函数
  • 5、子类非静态资源
  • 6、子类构造函数

猜你喜欢

转载自blog.csdn.net/qq_34560242/article/details/81002744