JVM类装载过程
- 加载-即今天我们要学习的部分,链接部分有机会再研究
- 链接
包括:
- 验证(Verification),字节码是否满足规范要求
- 准备(Preparation),内存分配,常量池初始化
- 解析(Resolution),解析类、接口、字段、方法等符号引用
- 初始化,执行类的初始化方法
- 研究参考:JVM规范
一、类加载过程
- 程序是依靠 ,多个Java类共同协作完成的
- 程序是依靠多个Java类共同协作完成的
- JVM依据classpath执行的类库的顺序来查找类
1.1引出潜在的问题
- 如何找到正确的类,如classpath路径的前后?
- 如何避免恶意的类,如一个假的String类
- 加载的顺序,如先加载父类,还是子类
- ……
待会将会根据这些问题进行一一梳理
二、类加载器ClassLoader
2.1作用:
负责查找、加载、校验字节码的应用程序
2.2包名:
java.lang.ClassLoader
2.3 重要函数及成员
load(String className)
根据名字加载一个类,返回类的实例defineClass(String name, byte[] b, int off, int len)
将一个字节流定义一个类将,这个流命名为name注册到虚拟机中findClass(String name)
查找一个类findLoadedClass(String name)
在已加载的类中,查找一个类- 成员变量
ClassLoader parent;
三、JVM四级类加载器
- 启动类加载器(
Bootstrap
), 系统类rt jar(JDK包中的类) - 扩展类加载器(
Extension
),jre/lib/ext - 应用类加载器(
App
), classpath,主要加载应用程序上配置的classpath上的类 - 用户自定义加载器(
Plugin
), 程序自定义 自己定义的
3.1类加载机制-双亲委托模型
- 首先判断是否已经加载
- 若没有,赵父加载器加载
- 若还没有,由当前加载器加载
我们看一下loadClass的源码
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 {//q启动类加载器在C层,name为null
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;
}
}
3.2举例:
就是我们都知道,假如我们函数中使用一个String类,String类是java JDK中的核心类,那假如我们自己定义一个String类?系统是怎么保证只加载使用JDK中的String类,而不使用我们定义的String类呢?
答案: 就是我们上述的双亲委托机制,我们应用中的使用到的类,优先使用到当前应用类加载器(APPClassLoader)的父类加载器,依次往上推,最后使用的我们的启动类加载器 (Bootstrap)来加载,启动类加载器主要加载JDK中的核心类,所以我们使用的JDK中的String类。
3.3优点:
避免我们的JDK核心类被篡改,冒充。JVM的四级加载器和双亲委托机制使得JDK的核心类得到优先加载–引出的问题我们已经解决完了
3.4缺点:
这种双亲委托机制也带了一些问题例如核心的一些JDK的类,无法使用我们自定义的类,JDK中就有这样的类:例如XMLparser JDBC等
当然解决方法还是有的:
添加虚拟机参数 :Xbootclasspath/a:path,将我们自己的类路径配置哪位Bootstrap等级
四、自定义类加载器
简单了解,即我们想要让程序加载我们想要加载的类
-
我看到Plugin时候想到的是Android开发中的插件,我们只要在AS的项目配置中配置我们需要的插件参数,我们通过gradle进行apk的编译,那么插件里面的内容肯定也要放进apk中。
-
所以我猜测,*AS里面的gradle应该和我们这里的自定义加载路径类加载器是有点关系的,应该说gradle用到了**自定义类加载器 **
4.1两种自定义类加载器
- 自定义加载路径
- 自定义类加载器
4.1.1自定义加载路径
- 弥补加载搜索路径静态的不足
- JDK提供URLClassLoader,从多个URL(jar或者目录)中加载类
//URL支持http、https、file、jar四种协议
URL url = new URL(“xxxxxx”);
URLClassLoader loader = new URLClassLoader(new URL[]{url});
Class<?> c = loader.loadClass("xxx类名");
4.1.2自定义类加载器
- 继承ClassLoader类
- 重写findClass(String name)方法
还可以在加载过程中修改字节码文件,完成加密解密操作
自由度比自定义加载路径强,可自行研究