本节内容是为了给下节内容做知识铺垫,所以如果要需要了解热修复技术,本节内容的知识点必不可少。
一个类在被加载到内存之前要经过加载、验证、准备等过程。经过这些过程之后,虚拟机才会从方法区将代表类的运行时数据结构转换为内存中的Class。
我们这节内容的重点在于一个类是如何被加载的,所以我们从类的加载入口开始。
类的加载是由虚拟机触发的,类的加载入口位于ClassLoader的loadClassInternal()方法:
// This method is invoked by the virtual machine to load a class.
private Class<?> loadClassInternal(String name)
throws ClassNotFoundException
{
// For backward compatibility, explicitly lock on ‘this‘ when
// the current class loader is not parallel capable.
if (parallelLockMap == null) {
synchronized (this) {
return loadClass(name);
}
} else {
return loadClass(name);
}
}
这段方法还有段注释说明:这个方法由虚拟机调用用来加载一个类。我们看到这个类的内部最后调用了loadClass()方法。那我们进入loadClass()方法看看:
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
loadClass()方法方法内部调用了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 {
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;
}
}
loadClass()方法大概做了以下工作:
- 首先查找该类是否已经被加载.
- 如果该ClassLoader有父加载器,那么调用父加载器的loadClass()方法.
- 如果没有父加载器,则调用findBootstrapClassOrNull()方法进行加载,该方法会使用引导类加载器进行加载。普通类是不会被该加载器加载到的,所以这里一般返回null.
- 如果前面的步骤都没找到,那调用自身的findClass()方法进行查找。
好,ClassLoader的findClass()方法是个空方法,所以这个过程一般是由子加载器实现的。Java的加载器这么设计是有一定的渊源的,感兴趣的读者可以自行查找书籍了解。
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
在Android中,ClassLoader的直接子类是BaseDexClassLoader,我们看一下BaseDexClassLoader的findClass()实现:
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class clazz = pathList.findClass(name);
if (clazz == null) {
throw new ClassNotFoundException(name);
}
return clazz;
}
Tips: 有需要虚拟机以及类加载器全套代码的,请使用以下命令克隆:
git clone https://android.googlesource.com/platform/dalvik-snapshot
相关代码位于项目的ics-mr1分支上。
看到这里我们可以知道,Android中类的查找是通过这个pathList进行查找的,而pathList又是个什么鬼呢?
在BaseDexClassLoader中声明了以下变量:
/** structured lists of path elements */
private final DexPathList pathList;
所以我们可以看看DexPathList的findClass()方法做了什么:
public Class findClass(String name) {
for (Element element : dexElements) {
DexFile dex = element.dexFile;
if (dex != null) {
Class clazz = dex.loadClassBinaryName(name, definingContext);
if (clazz != null) {
return clazz;
}
}
}
return null;
}
这里通过遍历dexElements中的Element对象进行查找,最终走的是DexFile的loadClassBinaryName()方法:
public Class loadClassBinaryName(String name, ClassLoader loader) {
return defineClass(name, loader, mCookie);
}
private native static Class defineClass(String name, ClassLoader loader, int cookie);
到此为止,我们就将类的加载过程梳理完了。
下一节课我们将会介绍如何实现类的替换。