JVM类加载机制

一、内置类加载器
1.跟加载器(c++代码的一段加载逻辑)
2.扩展类加载器 Launcher.ExtClassLoader
3.应用类加载器 Launcher.AppClassLoader
三个内置加载器是逻辑上的父子关系,并非Java语法上的继承关系,具体详见sun.misc.Launcher构造方法,ExtClassLoader实例通过传参设置为AppClassLoader的parent属性。

二、双亲委派
双亲委派的具体过程为,自下而上检查是否加载过某类,有则直接返回,没有则委派给父加载器(详见java.lang.ClassLoader#loadclass),加载过程自上而下(详见java.net.URLClassLoader#findClass)

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;
        }
    }
protected Class<?> findClass(final String name)throws ClassNotFoundException {
        final Class<?> result;
        try {
            result = AccessController.doPrivileged(
                new PrivilegedExceptionAction<Class<?>>() {
                    public Class<?> run() throws ClassNotFoundException {
                        String path = name.replace('.', '/').concat(".class");
                        Resource res = ucp.getResource(path, false);
                        if (res != null) {
                            try {
                                return defineClass(name, res);
                            } catch (IOException e) {
                                throw new ClassNotFoundException(name, e);
                            }
                        } else {
                            return null;
                        }
                    }
                }, acc);
        } catch (java.security.PrivilegedActionException pae) {
            throw (ClassNotFoundException) pae.getException();
        }
        if (result == null) {
            throw new ClassNotFoundException(name);
        }
        return result;
    }

疑问:双亲委派为什么要为什么要向上委派再向下加载,而不直接从上往下加载?
应用程序自定义类大多是由自定义类加载器或者AppClassLoader加载,只需在第一次加载的时候走双亲委派,之后从下判断加载过即可直接返回。

三、打破双亲委派
1.SPI机制(后续再补充)
2.自定义类加载器
自定义类加载器继承ClassLoader,重写findClass方法(会调用defineClass)。自定义类加载器会默认以AppClassLoader为父加载器,具体实现是在ClassLoader的构造函数中默认给parent属性设置

protected ClassLoader() {
	this(checkCreateClassLoader(), getSystemClassLoader());
}
public static ClassLoader getSystemClassLoader() {
    initSystemClassLoader();
    if (scl == null) {
        return null;
    }
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkClassLoaderPermission(scl, Reflection.getCallerClass());
    }
    return scl;
}
private static synchronized void initSystemClassLoader() {
    if (!sclSet) {
        if (scl != null)
            throw new IllegalStateException("recursive invocation");
        sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
        if (l != null) {
            Throwable oops = null;
            scl = l.getClassLoader();
            try {
                scl = AccessController.doPrivileged(
                    new SystemClassLoaderAction(scl));
            } catch (PrivilegedActionException pae) {
                oops = pae.getCause();
                if (oops instanceof InvocationTargetException) {
                    oops = oops.getCause();
                }
            }
            if (oops != null) {
                if (oops instanceof Error) {
                    throw (Error) oops;
                } else {
                    // wrap the exception
                    throw new Error(oops);
                }
            }
        }
        sclSet = true;
    }
}

再参见Launcher内

public ClassLoader getClassLoader() {
	return this.loader;
}

Launcher的构造函数内

try {
	this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
	throw new InternalError("Could not create application class loader", var9);
}

即AppClassLoader。

打破双亲委派即不委派,使用自定义类加载器加载指定类,重写loadClass方法,修改委派逻辑,不调用parent的loadClass方法向上委派。

@Override
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) {
        // 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.getFindClassTime().addElapsedTimeFrom(t1);
    	sun.misc.PerfCounter.getFindClasses().increment();
    }
    if (resolve) {
    	resolveClass(c);
    }
	return c;
}

注意:上述写法运行会报错Object not found,因为加载一个类的时候,会加载其父类,没有显式父类即以Object为父类,而Object是由内置类加载器加载,不向上委派则自定义类加载器找不到Object.class。此处做一个尝试,把JDK内部的Object.class文件复制到自定义的java.lang包下再次运行依然报错,因为类加载子系统的沙箱安全机制禁止(详见源码内加载逻辑之前的一系列安全校验)。这里简单处理根据类名做区别:

@Override
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) {
        // If still not found, then invoke findClass in order
        // to find the class.
        long t1 = System.nanoTime();
        if (!name.startsWith("com.zs.classload")){//自定义类的包名
        	c = this.getParent().loadClass(name);
        }else {
        	c = findClass(name);
        }
        // this is the defining class loader; record the stats
        sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
    	sun.misc.PerfCounter.getFindClasses().increment();
    }
    if (resolve) {
    	resolveClass(c);
    }
	return c;
}

这样就实现指定类仅由自定义类加载器加载,不向上委派,JDK原生类依然遵循双亲委派模式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值