您现在的位置是:主页 > news > 网站首页效果图/google入口

网站首页效果图/google入口

admin2025/6/15 2:47:54news

简介网站首页效果图,google入口,西安seo培训哪个好,wordpress 获取当前用户信息目录前言一、双亲委派机制二、浅析类加载器1.类加载器的关系2.方法分析总结后记补充前言 最近在学习类加载机制,看了一些JDK源码,记录一下自己的认识 一、双亲委派机制 老生常谈了属于是,加载器会先让他的父加载器先进行加载,如…

网站首页效果图,google入口,西安seo培训哪个好,wordpress 获取当前用户信息目录前言一、双亲委派机制二、浅析类加载器1.类加载器的关系2.方法分析总结后记补充前言 最近在学习类加载机制,看了一些JDK源码,记录一下自己的认识 一、双亲委派机制 老生常谈了属于是,加载器会先让他的父加载器先进行加载,如…

目录

  • 前言
  • 一、双亲委派机制
  • 二、浅析类加载器
    • 1.类加载器的关系
    • 2.方法分析
  • 总结
  • 后记
    • 补充


前言

最近在学习类加载机制,看了一些JDK源码,记录一下自己的认识



一、双亲委派机制

老生常谈了属于是,加载器会先让他的父加载器先进行加载,如果父加载器抛出ClassNotFound,自己再进行加载。

康康源码>>

ClassLoader类的loadClass源码,它会调用loadClass(String, Boolean)方法

	public Class<?> loadClass(String name) throws ClassNotFoundException {return loadClass(name, false);}

ClassLoader类的loadClass(String, Boolean)方法,是一个protected方法
英文注释是源码里的

	protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loaded//findLoadedClass这个方法是查缓存的Class<?> c = findLoadedClass(name);//如果缓存有就判断是否要解析然后返回了,如果缓存没有if (c == null) {long t0 = System.nanoTime();try {//如果父加载器不是bootstrap,则让父加载器执行loadClass方法if (parent != null) {//父加载器的loadClass会递归丢给他的父加载器c = parent.loadClass(name, false);} else {//如果父加载器是bootstrap,则执行下面这个方法,这个方法会调用findBootstrapClass(String)方法,这是个native方法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仍然是null说明父加载器加载不了)//那么调用自己的findClass(String)方法,在ClassLoader类里findClass是个空方法,需要自己在子类重写c = findClass(name);//下面三行我也不知道在做什么,不过估计跟类加载没关系// this is the defining class loader; record the statsPerfCounter.getParentDelegationTime().addTime(t1 - t0);PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);PerfCounter.getFindClasses().increment();}}//如果resolve为true,会对这个类进行解析//但是调loadClass(String)方法传到这的resolve是falseif (resolve) {resolveClass(c);}return c;}}
至此我们就明白了双亲委派模型的原理
findClass里是自己的加载逻辑,loadClass方法的作用就是丢给父类。如果loadClass成功了就返回加载的类,如果loadClass失败了就调findClass方法,这就是双亲委派的:每个儿子都很懒,有活先给父亲做,父亲做不了自己再做。
顺便也明白了为什么官方不推荐重写loadClass方法,而建议重写findClass方法:如果把加载逻辑写在loadClass方法里面,那loadClass方法就不会向上请求父加载器加载了,就破坏了双亲委派

二、浅析类加载器

1.类加载器的关系

双亲委派模型分三层,这都是老生常谈了
顶层的bootstrap启动类加载器
第二层的扩展类加载器(JDK9之后好像改名叫platform平台类加载器?)
第三层的AppClassLoader系统类加载器

但是分析他们的方法,要看继承关系

//最顶层是ClassLoader类
public abstract class ClassLoader {...}
//第二层是SecureClassLoader类
public class SecureClassLoader extends ClassLoader {...}
//第三层是URLClassLoader类和BuiltinClassLoader类
public class URLClassLoader extends SecureClassLoader implements Closeable {..}
//第四层是AppClassLoader类和PlatformClassLoader类,他俩继承自BuiltinClassLoader类//App和Platform是java.base下的jdk.internal.loader包下的ClassLoaders类里的内部类,继承BulitinClassLoader类。这是JDK11,JDK8之前他俩好像在什么sun.misc.Launcher类里。

2.方法分析

ClassLoader类:有loadClass方法,findClass为空,有四个defineClass方法
SecureClassLoader类:重写了两个defineClass方法
URLClassLoader类:重写了findClass方法,重写了一个defineClass方法

首先看看URLClassLoader类的findClass方法
	protected Class<?> findClass(final String name)throws ClassNotFoundException{final Class<?> result;try {//这个内部类我是没看懂result = AccessController.doPrivileged(new PrivilegedExceptionAction<>() {public Class<?> run() throws ClassNotFoundException {//把包名的.替换为路径符号/,然后在最后加上.classString path = name.replace('.', '/').concat(".class");//ucp是这个类的一个final修饰的URLClassPath变量//去看了看URLClassPath类的源码也没看懂,总之就是返回了一个Resource的实例Resource res = ucp.getResource(path, false);if (res != null) {try {//调用URLClassLoader类自己重写的defineClass方法,稍后分析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;}

可以看到findClass虽然一顿操作,但是核心还是调用defineClass方法,接下来看一下URLClassLoader类重写的defineClass方法

	private Class<?> defineClass(String name, Resource res) throws IOException {long t0 = System.nanoTime();int i = name.lastIndexOf('.');URL url = res.getCodeSourceURL();if (i != -1) {String pkgname = name.substring(0, i);// Check if package already loaded.Manifest man = res.getManifest();if (getAndVerifyPackage(pkgname, man, url) == null) {try {if (man != null) {definePackage(pkgname, man, url);} else {definePackage(pkgname, null, null, null, null, null, null, null);}} catch (IllegalArgumentException iae) {// parallel-capable class loaders: re-verify in case of a// race conditionif (getAndVerifyPackage(pkgname, man, url) == null) {// Should never happenthrow new AssertionError("Cannot find package " +pkgname);}}}}//又是一顿操作,前面的估计在找包吧,没细看// Now read the class bytes and define the classjava.nio.ByteBuffer bb = res.getByteBuffer();if (bb != null) {// Use (direct) ByteBuffer:CodeSigner[] signers = res.getCodeSigners();CodeSource cs = new CodeSource(url, signers);PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0);return defineClass(name, bb, cs);} else {byte[] b = res.getBytes();// must read certificates AFTER reading bytes.CodeSigner[] signers = res.getCodeSigners();CodeSource cs = new CodeSource(url, signers);PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0);return defineClass(name, b, 0, b.length, cs);}}

总之URLClassLoader类的defineClass最终也是调用父类SecureClassLoader的defineClass方法,接着我们再康康SecureClassLoader类的defineClass方法

	protected final Class<?> defineClass(String name,byte[] b, int off, int len,CodeSource cs){return defineClass(name, b, off, len, getProtectionDomain(cs));}protected final Class<?> defineClass(String name, java.nio.ByteBuffer b,CodeSource cs){return defineClass(name, b, getProtectionDomain(cs));}

没什么意思,其实也就是根据URLClassLoader传来的参数,调用了getProtectionDomain(CodeSource)这个方法之后,把返回值作为参数,调用了父类ClassLoader的defineClass方法
那么我们再康康ClassLoader类的defineClass方法,ClassLoader类的define方法有好几个,这里随便选了其中一个

	protected final Class<?> defineClass(String name, byte[] b, int off, int len,ProtectionDomain protectionDomain)throws ClassFormatError{protectionDomain = preDefineClass(name, protectionDomain);String source = defineClassSourceLocation(protectionDomain);Class<?> c = defineClass1(this, name, b, off, len, protectionDomain, source);postDefineClass(c, protectionDomain);return c;}

更没意思了,虽然又是一顿操作,但是核心还是调用了defineClass1这个native方法,ClassLoader类还有defineClass2和defineClass0这两个本地方法。
至此,我们大概就知道类加载的逻辑是怎么回事了,URLClassLoader类的findClass方法经过一顿操作,最终调用了native方法defineClass0或1,2


接下来是AppClassLoader类的loadClass方法(他也未重写findClass)

@Overrideprotected Class<?> loadClass(String cn, boolean resolve)throws ClassNotFoundException{// for compatibility reasons, say where restricted package list has// been updated to list API packages in the unnamed module.SecurityManager sm = System.getSecurityManager();if (sm != null) {int i = cn.lastIndexOf('.');if (i != -1) {sm.checkPackageAccess(cn.substring(0, i));}}return super.loadClass(cn, resolve);}

App没有交给他的父加载器去加载,而是尝试调用父类(BuiltinClassLoader)的loadClass方法
BuiltinClassLoader的loadClass方法如下:

@Overrideprotected Class<?> loadClass(String cn, boolean resolve)throws ClassNotFoundException{Class<?> c = loadClassOrNull(cn, resolve);if (c == null)throw new ClassNotFoundException(cn);return c;}

调用了loadClassOrNull方法(先要判断一个loadedModule然后里面一堆逻辑),然后如果parent!=null的话,这个方法会请求parent调用loadClassOrNull方法。看起来像是双亲委派,但是我没搞懂这是在做什么:AppClassLoader的parent是PlatformClassLoader(以前好像叫ExtClassLoader?),这个Platform他没有loadClassOrNull方法,而且这里是父关系不是父类,parent没有就是没有了,不会继续向上查找有这个方法的父类。
如果加载仍然失败,BuiltinClassLoader会调用findClassOnClassPathOrNull(String)这个方法,这个方法最终会调用defineClass方法,这样就完成了加载。



总结

分析了源码之后,对于类加载的5个过程也有了一定的认识,类加载的五个过程:加载、验证、准备、解析、初始化
加载是调用loadClass之类的方法、然后验证大概是那什么SecureClassLoader负责的,准备这一步我也不清楚、然后解析就是调用resolveClass函数,初始化这个只有Class.forName会完成,defineClass0或1或2不做初始化(大概)
本文之所以说是浅析,一是因为我个人才疏学浅,二是因为分析到最后,发现怎么什么都是调用的本地方法啊,这玩意又分析不了,所以最后也只是知道“奥,他调了native方法”,只称得上浅析。



后记

补充

在继承URLClassLoader类的MyLoader类里,我重写了findClass方法和loadClass方法,findClass方法的内容仅仅是调用super.findClass,但是我偶然间发现,当我仅调用findClass时,loadClass也会被调用。
我感觉非常的迷惑和不解,因为我没看到有任何地方会去调用Myloader类的loader方法,但是实际上他就是被执行了,为什么呢?

String className = Thread.currentThread().getStackTrace()[2].getClassName();//调用的类名
String methodName = Thread.currentThread().getStackTrace()[2].getMethodName();//调用的方法名
int lineNumber = Thread.currentThread().getStackTrace()[2].getLineNumber();//调用的行数
System.out.println(className+" "+methodName+" "+lineNumber);

通过这几句查看调用栈的代码,我发现在defineClass1和forName0等本地方法调用了MyLoader类的loadClass方法,但是至于具体代码,我就无从分析了。