一、从Hello World轻松理解类加载的基本过程
1.类加载子系统整体工作过程
大白话:
符号引用 - 相当于建房子的图纸,在字节码文件中
直接引用 - 建房子,在Java的内存模型中
这里需要注意下面的代码
这里为什么先在静态代码块给a赋值20,下面才是定义静态字段a=10,竟然不报错呢,而且a=10
原因:这个因为在执行初始化阶段之前,链接阶段二(准备阶段)会给类的变量分配内存并赋予初始值,结合这里来说,a的初始值一开始为0,执行到静态代码块a的值变为了20,继续执行到第9行,a的值变为了10,最终输出为10。
二、深入剖析类加载器的工作原理
验证不同的类由不同的加载器加载
测试不同的加载器加载文件的路径有哪些
大白话:
扩展类加载器加载的类文件,相对JVM而言,不是特别重要,但是又比我们写的代码重要。
三、从JVM源码层面理解加载器工作原理- 这里有个单例模式
点开Launcher
启动类加载器的入口
大白话:
创建ExtClassLoader之后,将其以引用参数的方式传递到下面代码,创建的时候会参考ExtClassLoader信息,创建出AppClassLoader,这里就不是层次关系,也不是面向对象的那种父子或继承关系。这里看到扩展类加载器没有和引导类加载器建立关联,这是引导类加载器是C++实现的,所以无法通过Java代码看到,但创建扩展类加载器的时候,肯定会参考利用引导类加载器的信息的。
1.即不同的类加载器之间的关系是parent关系,但这个parent关系不是父子关系或继承关系,而是上级给下级参数信息,创建出下级的关系;
2.创建扩展类加载器的时候,用到了单例模式;
3.不同的加载器加载文件路径不一样;
4.AppClassLoader没有使用单例模式。
重要事项:
1.创建ExtClassLoader使用了单例模式,这里用了双重检测锁保证整个JVM只能有一个ExtClassLoader;
真正执行创建ExtClassLoader的方法是createExtClassLoader():
这里有个getExtDirs(),获得扩展路径的方法
创建AppClassLoader,点击AppClassLoader代码
这里的java.class.path就是我们在配置JDK环境变量时,对应配置的CLASSPATH;
四、图解双亲委派机制的核心流程
大白话 :
收到一个新的类,最底层的系统类加载器并不直接加载,而是向上委托给扩展类加载器,而扩展类加载器拿到新类后,也不执行加载,继续向上委托给引导类加载器,引导类加载器会执行加载,假如能够加载成功,结束退出,如不能加载,向下返回个null,扩展类加载器检测到上层加载器不能加载,它会执行加载,假如加载成功,结束退出,如不能加载,继续向下返回个null,系统类加载器检测到上层加载器不能加载,系统类加载器会执行加载。
五、从JVM源码理解双亲委派机制-这里有递归的经典引用
继续看Launcher代码
loadClass是加载器的核心实现
实现双亲委派的核心代码,这里是个递归
代码解析:
判断类是否已经加载
这里是判断是否已经装载过的代码,再朝下看超出了Java代码内容,是看不到的,如果需要继续研究下去,需要下载Hotspot的C++代码。
六、如何打破双亲委派机制
点击Java.net.UrlFindClass实现
我们在操作系统中都是以”/"的方式作为路径,Java中是”.“作为路径,这里将Java文件的目录”.“替换成了”/“,然后在最后末尾追加了".class",这样就能找到一个类文件了。
这里是一个通过字节流读文件的操作
点击defineClass
再点击里边的defineClass
点击defineClass1,发现跑出来Java的范畴
总结:
代码跟到最后发现,如果加载一个类的最终代码,不在Java中,我们也就不关心加载一个类的最终实现了,但是如果我们要实现一个自定义的类加载器,就要参考这个过程,继承ClassLoader,调用它的findClass方法,这个方法就会自动帮你装载Java类文件。
实现自定义的类加载器
七、【经典案例】深入浅出Tomcat的沙箱安全机制