一步步手动实现热修复(二)-类的加载机制简要介绍

本节内容是为了给下节内容做知识铺垫,所以如果要需要了解热修复技术,本节内容的知识点必不可少。

一个类在被加载到内存之前要经过加载、验证、准备等过程。经过这些过程之后,虚拟机才会从方法区将代表类的运行时数据结构转换为内存中的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);

到此为止,我们就将类的加载过程梳理完了。

下一节课我们将会介绍如何实现类的替换。

时间: 2024-08-24 10:09:21

一步步手动实现热修复(二)-类的加载机制简要介绍的相关文章

一步步手动实现热修复(三)Class文件的替换

本节课程主要分为3块: 1.一步步手动实现热修复(一)-dex文件的生成与加载 2.一步步手动实现热修复(二)-类的加载机制简要介绍 3.一步步手动实现热修复(三)-Class文件的替换 本节示例所用到的任何资源都已开源,项目中包含工程中所用到代码.示例图片.说明文档.项目地址为: https://code.csdn.net/u011064099/sahadevhotfix/tree/master 在上一节了解了基本的类加载原理之后,我们这一节开始对工程内部的类实行替换. Tips: 本章主要依

jvm系列(一):java类的加载机制

java类的加载机制 原文:http://www.cnblogs.com/ityouknow/p/5603287.html 1.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构.类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口. 类加载器并不需要等到某个

深入java虚拟机学习 -- 类的加载机制(续)

昨晚写 深入java虚拟机学习 -- 类的加载机制 都到1点半了,由于第二天还要工作,没有将上篇文章中的demo讲解写出来,今天抽时间补上昨晚的例子讲解. 这里我先把昨天的两份代码贴过来,重新看下: class Singleton { private static Singleton singleton = new Singleton(); //第一份代码的位置 public static int counter1; public static int counter2=0; private s

Java温故而知新(10)类的加载机制

类加载是Java程序运行的第一步,研究类的加载有助于了解JVM执行过程,并指导开发者采取更有效的措施配合程序执行. 研究类加载机制的第二个目的是让程序能动态的控制类加载,比如热部署等,提高程序的灵活性和适应性. 1.类加载机制 我们来了解一下虚拟机如何加载Class文件. 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被直接使用的java类型,这就是虚拟机的类加载机制. 类的生命周期包括加载(Loading).验证(Verification).准

类的加载机制(四)

这一章我们主要是对双亲委派机制进行详细讲解: 前面我们知道类加载有系统自带的3种加载器,也有自定义的加载器,那么这些加载器之间的关系是什么,已经在加载类的时候,谁去加载呢?这节,我们将进行讲解. 一.双亲委派机制 JVM的ClassLoader采用的是树形结构,除了BootstrapClassLoader以外?每个ClassLoader都会有一个parentClassLoader,用户自定义的ClassLoader默认的parentClassLoader是SystemClassLoader,当然

双亲委派模型,类的加载机制,搞定大厂高频面试题

看过这篇文章,大厂面试你「双亲委派模型」,硬气的说一句,你怕啥? 读该文章姿势 打开手头的 IDE,按照文章内容及思路进行代码跟踪与思考 手头没有 IDE,先收藏,回头看 (万一哪次面试问了呢) 需要查看和拷贝代码,点击文章末尾出「阅读原文」 文章内容相对较长,所以添加了目录,如果你希望对 Java 的类加载过程有个更深入的了解,同时增加自己的面试技能点,请耐心读完...... 双亲委派模型 在介绍这个Java技术点之前,先试着思考以下几个问题: 为什么我们不能定义同名的 String 的 ja

java类的加载机制

文章来源: 转载自纯洁的微笑 原文链接:http://www.cnblogs.com/ityouknow/p/5603287.html 1.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构.类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口. 类加载器并不

1.类的加载机制_继承类的加载(一个小的Demo)说明

今天我们先来一个小的Demo来了解类的加载顺序. 1 public class ClassLoaderTest { 2 public static void main(String[] args) { 3 System.out.println("*******************测试类的加载顺序开始*******************"); 4 new SubClass(); 5 System.out.println("*******************测试类的加载

类的加载机制和反射——一、类的加载、连接和初始化

类的加载.连接和初始化 1.JVM和类 (1)当调用java命令运行某个Java程序时,该命令会启动一个Java虚拟机进程,不管该Java程序有多么复杂,该程序启动了多少线程,他们都处于该Java虚拟机进程里. (2)同一个JVM的所有线程.所有变量都处于同一个进程里,他们都使用该JVM进程的内存区. 下面的ATast1和ATest2的输出结果分别是7和6,因为这两个不是位于同一个JVM中的. public class A { //定义该类的类变量 public static int a = 6