var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?9ee4cf84cdf5f7446d26edc5598e4a6d";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
我是原始Java程序员,在一篇之前的文章中,我们提到了关于JAVA网络的一个话题,现在我看到有关虚拟机的博客或者文章,也提到了 类加载器(ClassLoader),我想做一个简单的学习记录,我之前看网络文章,有时候不太明白为什么一直要依赖于父加载器的代理模型,这一一来不知道的时候就会去看一下,大多数时间我们其实并没有遇到双亲委托模式的问题,也或者说在编程工作中一般使用第三方类库很少会涉及到自定义类加载器,不过自定义类加载器也是比较常见的一种技术,在Android开发中应用广泛,另外有时候我们在项目中也会用到自定义类加载器来做一些模块打包隔离的工作,所以重新探索一下类加载器的工作
原则是值得的。在类加载过程中,如果遇到了几个比较关键的步骤,分别是,在类加载过程中现在只关注,父加载器代理类加载过程,Hot
SPot虚拟机中类加载器属于java.lang.ClassLoader的子类,Java程序在运行时,需要对类进行加载,类加载器用于加载class文件,且class文件加载到内存后会经过验证、准备等步骤,然后才能被创建为Class对象来供程序使用,类加载的过程主要包括验证、准备、解析、初始化、卸载5个阶段,其中前4个是加载时的过程,那类加载过程的时机是当程序第一次通过调用static、instanceof或者new等关键字要求加载类时,虚拟机需要的步骤包括:检查该类的类加载器是否有权利加载,每个类型的类加载器都是由父加载器来代理的,父加载器先进行加载如果没有找到,或者父加载器为null说明是根加载器去进行加载,而之类加载器是通过双亲委托机制进行加载的,即在代理过程中,先由父加载器进行尝试加载,如果父加载器没有加载成功那么子加载器才会尝试去加载,这样保证了内置类加载器的优先性, 我还没有理解这里的验证部分,不过可以先把代理机制给解释清楚,所以这里在学习类加载器的过程中,我们知道了两类类加载器:根加载器和引导加载器,根加载器也叫做启动类加载器,负责加载核心类库的class文件,引导加载器也叫做系统加载器,负责加载扩展类库或者应用类库,根引导加载器是JVM运行时数据区(运行时常量池)的一部分,他本身不是Java类,是由C++编写的,因此根引导加载器是唯一的没有继承 这里,我的学习笔记的观点有些混乱了,这时候再次小白思考一下,虚拟机规定所有加载类加载器必须继承定义各自的父类加载器,启动类加载器是由原生代码来实现,而不是继承ClassLoader类的,这可能是一个错误的理解,定位问题的来源:我们看到HotSpot的源代码中,ClassLoader是一个Java类,但是双亲委托时,启动类加载器是由C++编写,代码 this.handlesPackage( name, package2); this.allowStackFromClassLoaderAssertion;; 而用户自定义类加载器是继承ClassLoader类的,所以啊,BootstrapClassLoader并不是ClassLoader的实例,而且它也没有父类加载器,BootstrapClassLoader是JVM运行时数据区(运行时常量池)的一部分,ClassLoader.getSystemClassLoader()是获取系统类加载器,它就是扩展类加载器,ClassLoader.getAppClassLoader(),其实是应用程序类加载器,他是系统类加载器的父加载器,而系统类加载器是用来加载用户的Classpath路径下的类的,系统类加载器的父加载器是扩展类加载器,扩展类加载器的父加载器是启动类加载器,而启动类加载器就没有父加载器了。类加载器的工作机制是通过类加载器的代理模式来实现的,Java程序中的类加载都是委托给父类加载器加载的,除非在加载之前父类已经加载过了或者找不到父类的类加载器,那么才是自己进行加载,类加载器的代理过程:
触发加载一个类时,首先判断该类型是否已经被加载,如果没有,则委托给父类的类加载器代理加载,如果父类加载器为空,则使用根引导加载器进行加载,否则则当前类加载器请求代理父类加载器加载 unpathed 其实对类加载器的代理过程面试题是常见的,这里所说的这个代理过程有双重含义,一个是我们定义不同类加载器功能划分,另一个是加载过程中的步骤,加载一个新的类型时,虚拟机先
检查该类型是否已经被加载,并且需要检查类加载器是否有权限加载,有两个检查过程:请求代理的类加载器需要考虑是否同意被代理的类由请求代理者的加载器加载。顺利的话,就会委托给父加载器代理加载,委派模型的校验过程主要有两个作用,第一个作用是降低代码的重复性,另一个作用是信任层次,
双亲委托机制的过程:首先,获取当前类的类加载器,如果类加载器为null,则使用BootstrapClassLoader加载,否则,获取类加载器中触发代理模式的父加载器,尝试由父加载器代理加载目标类,如果父加载器加载目标类失败,则当前类加载器继续调用父加载器尝试,所以是自上而下的加载过程,也就是从根加载器开始,逐层向下寻找由哪个类加载器能够加载,循环代理过程,可以有一点变化,主程序的类加载过程会先使用BootstrapClassLoader加载 core/
core/,然后扩展类加载器加载扩展目录中的类,appClassLoader加载用户类path上的类,所以在Java中加载类的顺序依次是这样的,BootstrapClassLoader,ExtensionClassLoader, AppClassLoader, 这三个方面是必须的,在双亲委托模式中我们可以发现,避免重复加载是这种工作机制所决定的,因为父加载器已经加载过了,那么子加载器就会跳过加载的步骤,直接返回,但是我们没请求代理的往往是自定义类加载器,这时需要检查启被代理者的父加载器,观察是否已经被加载过,或者启作用者加载,但是这里有一个关键点,自定义类加载器总是空指针异常,下面给出一个示例代码,APT惕的思考,proxy animal loader和自己的业务相关,

实例NameSpaceClassLoader extends ClassLoader,我们来加载一个自定NameSpaceClassLoader,它持有一个ClassLoader对象,这个 loader负责加载
ClassLoader loader = new NameSpaceClassLoader(new ClassLoader());
此时我们在NameSpaceClassLoader 中加载,如果加载失败返回null,如果是代理过程中的Comp lb 怎么理解委托过程,在Java中,类加载器在加载类时,需要父类加载器先进行加载,这是一个委托代理的过程,整个过程是从子到父进行的,子加载器收到加载请求,传递给父加载器,如果父加载器加载成功则告诉子加载器可以缓存类,如果父加载器加载失败则子加载器自行加载 这里有一个误区,在双亲委托模式中,子加载器收到请求后,先交给父加载器去加载, 也就是说,加载一个类,先是父加载器尝试,然后是子加载器,也就上层 直到BootstrapClassLoader 其实,字节码文件加载的基本步骤是:
对于字节码文件,加载器尝试找到该字节码文件的二进制字节码额,并定义出一个类加载器,然后读取其内部解析字节码,执行验证,为静态变量分配存储空间等,验证成功后,转换为class对象进行使用,在加载类,接口或一个数组类型时,如果有父类或实现类或元素类型,需要先加载他们,在代理模式中,双亲委托模式是这样定义的:当某个类加载器收到请求去加载某个类时,它并不会自己去加载,而是将其委派给父类加载器,只有当父类加载器在搜索后返回该类未能找 请继续给出你的学习笔记
<