jvm类加载机制

一.类的加载流程

下面是一个简单的类

package com.company;

public class Test {
    public void hello()
    {
        System.out.println("hello");
    }
    public static void main(String[] args) {
        Test test=new Test();
        test.hello();
    }
}

通过java命令执行,流程如下
jvm类加载机制_第1张图片
loadClass加载类,它的整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载七个阶段。它们的顺序如下图所示:
jvm类加载机制_第2张图片
各个步骤主要的工作
加载:读取字节码文件,在内存中生成这个类的java.lang.Class对象,作为方法区的这个类的各种数据的访问入口
验证:验证字节码的正确性
准备:给类的静态变量分配内存,并赋默认值。如int类型,赋值为0
解析:将符号引用替换为直接引用,该阶段会把一些静态方法(符号引用)替换为执行数据内存的指针。这是静态链接过程。
初始化:将类的静态变量设为代码中指定的值,执行静态代码块static{代码}

二.类加载器

jvm类加载机制_第3张图片
启动类记载器:负责加载jre目录下lib核心类库,rt.jar、resources.jar等
扩展类加载器:负责加载jre目录下ext目录的类包
应用程序类加载器:负责加载ClassPath路径下的类包,主要是自己写的类
自定义加载器:负责加载用户自定义路径下的类包

类加载器初始化过程
jvm启动器创建实例sun.misc.Launcher。其构造方法中创建了扩展类加载器和应用类加载器。
jvm默认使用Launcher的getClassLoader()方法返回应用类加载器的的实例,加载我们的应用程序,并使用双亲委派的机制。

构造方法

public Launcher() {
        Launcher.ExtClassLoader var1;
        try {
        	//扩展类加载器
            var1 = Launcher.ExtClassLoader.getExtClassLoader();
        } catch (IOException var10) {
            throw new InternalError("Could not create extension class loader");
        }

        try {
        	//将load属性,设为应用类加载器
            this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
        } catch (IOException var9) {
            throw new InternalError("Could not create application class loader");
        }

        Thread.currentThread().setContextClassLoader(this.loader);
        String var2 = System.getProperty("java.security.manager");
        if (var2 != null) {
            SecurityManager var3 = null;
            if (!"".equals(var2) && !"default".equals(var2)) {
                try {
                    var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
                } catch (IllegalAccessException var5) {
                } catch (InstantiationException var6) {
                } catch (ClassNotFoundException var7) {
                } catch (ClassCastException var8) {
                }
            } else {
                var3 = new SecurityManager();
            }

            if (var3 == null) {
                throw new InternalError("Could not create SecurityManager: " + var2);
            }

            System.setSecurityManager(var3);
        }

    }

三.双亲委派机制

原理:当一个类加载器收到类加载任务,会先交给其父类加载器去完成,因此最终加载任务都会传递到顶层的启动类加载器,只有当父类加载器无法完成加载任务时,才会尝试执行加载任务
源码如下
其核心代码块通过递归方式不断调用parent.loadClass(),直到调用到启动类加载器结束。若c为null,则调用本加载器。

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) {
                    	//递归,调用其父级的loadClass方法
                        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;
        }
    }

双亲委派机制的好处
1.可以避免重复加载:父亲已经加载就不需要重复加载,确保唯一性
2.沙箱安全机制:防止核心API库被修改。如下操作会报错
在项目中新建java.lang包,在包下建String类,与核心库的String类文件一致
jvm类加载机制_第4张图片
调用main方法报错,找不到main方法。因为双亲委派的机制,java.lang.String的在启动类加载器得到加载,因为在核心jre库中有其相同名字的类文件,但该类中并没有main方法。这样就能防止黑客篡改核心API库。
jvm类加载机制_第5张图片

你可能感兴趣的:(java,jvm)