jvm - 类加载器

​ jvm加载字节码的第一步有个加载,加载首先就是根据类的全限定名找到类的二进制字节流。jvm将这一步暴漏了出来,我们可以通过实现自己的类加载器完成这一步。这样我们就可以动态的指定我们类所在的位置,网络,磁盘等等,或者动态生成也可以。

​ 通过这个特性,我们就有了实现热部署,代码加密等的思路。

分类

  • boot加载 ,javahome中的jar
  • extent加载 ,javahome中ext下的jar文件
  • app加载,程序运行的class文件
  • 自定义加载,自己指定

双亲委派(保证了核心类只加载一次)

  1. 双亲委派并不是继承,而是组合的方法
  2. 逻辑:
    1. 先检查是否加载过(native)
    2. 没有加载过的话,如果parent不空,让parent加载。如果parent空,让boot加载。
    3. 之后class还是为空,调用自己的findClass方法加载。
    4. 代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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;
}
}
  1. 我们知道了双亲委派的机制是在classLoader的loadClass方法中实现的,所以我们在实现自定义classlaoder的时候原则上不建议重写loadclass,而是重写findclass方法

Thinker大致原理

​ android虚拟机不是加载class文件的,而是dex文件。使用的是dexClassLoader,dexClassLoader中有一个List存放dex文件的路径,加载的时候会安顺序去加载,我们可以通过反射将修复好的dex文件放入list的首部,这样修复好的类会先被找到,那么运行的时候跑的就是修复好的类文件。