JVM学习(2):类加载器

什么是类加载器:

public class ClassInit {
    public static void main(String[] args) {
        ClassLoader c=ClassInit.class.getClassLoader();
    }
}

上面这个ClassLoader就是类加载器

打印c,注意到一个Launcher类:

sun.misc.Launcher$AppClassLoader@18b4aac2

进入Launcher类,注意到其中的两段代码:

var1 = Launcher.ExtClassLoader.getExtClassLoader();
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);

其中的ExtClassLoader和AppClassLoader有什么关系呢?

修改成如下代码:

public class ClassInit {
    public static void main(String[] args) {
        ClassLoader c = ClassInit.class.getClassLoader();
        while (c != null) {
            System.out.println(c);
            c = c.getParent();
        }
    }
}

打印:

sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@1540e19d

 于是得到了结论:AppClassLoader有一个父亲是ExtClassLoader

继续分析ClassLoader,注意到一段代码:

    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)

独占锁保证了同时只能加载一个类

然后检查这个类是否被加载过,如果被加载过直接返回;

如果没有被加载过,那么就递归加载他的父亲,直到没有父亲,最后执行findBootstrapClassOrNull()

往下看发现这样一段代码:

    // return null if not found
    private native Class<?> findBootstrapClass(String name);

这个函数没有函数体,被native修饰,代表调用一个本地方法

这里调用本地方法,根据操作系统的不同,调用的本地方法接口不同,然后调用本地方法库

得出结论:

一个类的加载顺序是:BootstrapClassLoader--->ExtClassLoader--->AppClassLoader

一个类的检查顺序是:AppClassLoader--->ExtClassLoader--->BootstrapClassLoader

为什么加载顺序要从BootstrapClassLoader开始呢?

写一个新代码,包名和类名与java源码的List一样:

package java.util;

public class List{
 public static void main(String[] args){
   xxxxxx
 }
}

发现无法运行,报错:在类java.util.List中找不到main方法

无法运行是非常合理的,否则一个黑客岂不是可以轻易植入病毒代码,然后通过自定义类加载器加入到JVM中

这种机制称为:双亲委任

目的:安全

1.父类如果可以加载,那么不允许子类加载

2.保证一个类只加载一次

判断两个对象是不是相等,最重要的条件是看是不是一个类加载器:

写一个代码:

package org.dreamtech.cl;

import java.io.IOException;
import java.io.InputStream;

public class ClassInit {
    //自定义类加载器
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        ClassLoader loader = new ClassLoader() {
            @Override
            public Class<?> loadClass(String name) throws ClassNotFoundException {
                try {
                    String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
                    InputStream is = getClass().getResourceAsStream(fileName);
                    if (is == null) {
                        return super.loadClass(name);
                    }
                    byte[] b = new byte[is.available()];
                    is.read(b);
                    return defineClass(name, b, 0, b.length);
                } catch (IOException e) {
                    throw new ClassNotFoundException();
                }
            }
        };
        String className = "org.dreamtech.cl.ClassInit";
        Object o1 = ClassInit.class.getClassLoader().loadClass(className).newInstance();
        Object o2 = loader.loadClass(className).newInstance();
        System.out.println(o1 == o2);
        System.out.println(o1.equals(o2));
        System.out.println(o1 instanceof org.dreamtech.cl.ClassInit);
        System.out.println(o2 instanceof org.dreamtech.cl.ClassInit);
        System.out.println(o1.getClass().getClassLoader());
        System.out.println(o2.getClass().getClassLoader());
    }
}

打印如下:

false
false
true
false
sun.misc.Launcher$AppClassLoader@18b4aac2
org.dreamtech.cl.ClassInit$1@14ae5a5

猜你喜欢

转载自www.cnblogs.com/xuyiqing/p/12468440.html