[Fat Tiger's Reverse Road] 01 - Detailed Explanation of Dynamic Loading and Class Loading Mechanism

I. Introduction

I have been aware of packing and unpacking before, using Fart and other unpacking tools directly, staying at the level of knowing what I don’t know why, so in order to prepare for the study of Android basic theory, we must first have a deep understanding of class loaders and The relationship between dynamic loading and dynamic loading. This article records the relationship and principle between class loader and dynamic loading. Due to the limited ability of the author, I will try my best to explain the relationship between the two in detail. If there is any error in this article, please Correct me, thanks~


Second, the class loader

The class loader mechanism in Android follows the parental delegation (parental loading) model like the JVM

Parent delegation/parent loading both refer to the same mechanism, just called it differently...

1. Parental delegation model

First, let’s take a look at the industry’s explanation of the parental delegation mechanism

1) When loading a .class file, it is recursively entrusted to the parent loader ParentClassLoader to load. If it has been loaded, it does not need to be loaded again. 2) If the
parent loader has not been loaded, continue to delegate to the parent loader To load, until the top of this link, if the top ClassLoader has not been loaded, try to load, if the loading fails, it will be handed back to the caller for loading step by step

It is very concise and clear, but it may be difficult for students who don’t understand. Here I probably drew a picture (the style of the painting is distorted, watch carefully...)

insert image description here

From the above picture combined with the text description, it should be almost understandable, right?
So let me say it again:

1) First check whether the current ClassLoader has loaded the class file, use the findLoadedClass method, if it has been loaded, return directly
2) If the current ClassLoader has not been loaded, and there is a parent class (judging that it is not the top-level ClassLoader), then delegate To load the parent class, use the parent.loadClass(name, false) method. At this time, it will be passed upwards, and then go to the parent ClassLoader to cycle step 1 until the top ClassLoader (3) If the parent ClassLoader is not loaded, try this
level ClassLoader loads, if the loading fails, it will be passed down, and handed over to the calling method to realize the loading of the .class file

At this point, you can probably understand, right?

Well, if you still don’t understand it here, it seems that I have too high expectations for my writing. The following is a passage I copied (from Baidu Google)

We want to load a class file, and we define a CustomerClassLoader class loader:
(1) First, it will judge whether its CustomerClassLoader has been loaded, and if it has been loaded, it will return directly,
(2) If it has not been loaded, it will call the parent class PathClassLoader to load , the parent class will also judge whether it has loaded it. If it has not loaded it, it will entrust it to the parent class BootClassLoader to load. (3)
This BootClassLoader is the top classLoader. It will also judge whether it has loaded it. If it has not loaded it, then It will call its own findClass(name) to load,
(4) If the top-level BootClassLoader fails to load, it will return the loading action to PathClassLoader,
(5) this PathClassLoader will also try to call findClass(name); to load , if the loading fails, it will continue to return to CustomClassLoader to complete the loading. This whole process feels like a recursive process, gradually going up and then gradually going down until the loading is successful. In fact, this String.class is in the system when it starts
. It has been loaded, we define a CustomerClassLoader to load, in fact, it is also loaded by the parent class

So why is there such a thing to limit the loading of class files?

(1) Prevent the same .class file from being loaded repeatedly
(2) Ensure the uniqueness of any class in the virtual machine. The class loader that loads it and the full class name of the class establish its uniqueness in the Java virtual machine Uniqueness
(3) Ensure that the .class file is not tampered with, and the loading logic of the system class can be guaranteed not to be tampered with through delegation

Here comes the knowledge point, how to ensure the uniqueness of a class?

Not only the full class name, but also the class loader that loads the class and the full class name of the class together determine the uniqueness in the jvm


2. Class loading mechanism in Android

1) Preloading of Android basic classes

First look at the startup of the Dalvik virtual machine (the picture is copied)
insert image description here

In plain language it goes like this:

  1. Bootloader start (power button start) to
  2. After the kernel starts the idle process
  3. After the Nativate layer executes the Init process
  4. The analysis executes init.rc, and inside it
  5. Called app_process (the basis of Xposed is to replace app_process), and then enter
  6. The framework layer generates the zygote process. When other app processes are started, the incubation of the process is carried out in zygote. Finally, our
  7. Each system_server process starts and ends

Of course, we have simplified a lot of processes, such as the main work of the zygote native process. I won’t go into details about these for now, and I will open a separate article to talk about zygote in the future~


Back to our class loader...

2) Android class loader hierarchical relationship and analysis

The following figure is a schematic diagram of the hierarchical relationship (don’t guess, I’m from Baidu), which clearly shows the relationship and hierarchy between the loaders, please see~

insert image description here

The ClassLoader types in Android are divided into system ClassLoader and custom ClassLoader.
Among them, the system ClassLoader includes three types: BootClassLoader, DexClassLoader, and PathClassLoader
(1) BootClassLoader: all Android systems on the Android platform will use BootClassLoader to preload commonly used classes when they start
(2) BaseDexClassLoader: the loading of the actual application layer class files, and the real Loading is entrusted to pathList to complete
(3) DexClassLoader: can load dex files and compressed files containing dex (apk, dex, jar, zip), and can install an uninstalled apk file, usually a custom class loader (4
) PathClassLoader: The class that can load system classes and applications, usually used to load the dex file of the installed apk
Supplement:
The native loader provided by Android is called the basic class loader, including: BootClassLoader, PathClassLoader, DexClassLoader, InMemoryDexClassLoader (introduced in Android 8.0 ), DelegateLastClassLoader (introduced in Android 8.1)

emm If you have a little taste, I think it will be ok at this point. The following will be the specific details. Are you ready (gripping)


3)BootClassLoader

The loader of the startup class is used to record the basic classes that the zygote process has preloaded. It can be speculated that it only needs to be loaded from the cache. This is the base class ClassLoader and finally an internal class. Due to the access rights of the package, the application layer has no way direct interview

Let's take a look at his source code

public abstract class ClassLoader {
    
    
    // ...省略
 
    class BootClassLoader extends ClassLoader {
    
    
        private static BootClassLoader instance;
        public static synchronized BootClassLoader getInstance() {
    
    
            if (instance == null) {
    
    
                instance = new BootClassLoader();
            }
            return instance;
        }
        public BootClassLoader() {
    
    
            super(null);
        }
 
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
    
    
            return Class.classForName(name, false, null);
        }
 
        // ...省略
        @Override
        protected Class<?> loadClass(String className, boolean resolve)
               throws ClassNotFoundException {
    
    
            Class<?> clazz = findLoadedClass(className);
            if (clazz == null) {
    
    
                clazz = findClass(className);
            }
            return clazz;
        }
 
        // ...省略
    }
}

In the source code analysis, we can see that BootClassLoader has no parent class loader. When the class cannot be retrieved from the cache, it directly calls its own findclass method. The findClass() method calls the Class.classForName method, and ZygoteInit.preloadClasses() , loading the base class is Class.forName()

ublic final class Class<T> implements java.io.Serializable,
                              GenericDeclaration,
                              Type,
                              AnnotatedElement {
    
    
    // ...省略
 
    public static Class<?> forName(String className)
                throws ClassNotFoundException {
    
    
        Class<?> caller = Reflection.getCallerClass();
        return forName(className, true, ClassLoader.getClassLoader(caller));
    }
 
    public static Class<?> forName(String name, boolean initialize,
                                   ClassLoader loader)
        throws ClassNotFoundException
    {
    
    
        if (loader == null) {
    
    
            loader = BootClassLoader.getInstance();
        }
        Class<?> result;
        try {
    
    
            result = classForName(name, initialize, loader);
        } catch (ClassNotFoundException e) {
    
    
            Throwable cause = e.getCause();
            if (cause instanceof LinkageError) {
    
    
                throw (LinkageError) cause;
            }
            throw e;
        }
        return result;
    }
 
    // 本地方法
    static native Class<?> classForName(String className, boolean shouldInitialize,
            ClassLoader classLoader) throws ClassNotFoundException;
 
    // ...省略
}

Looking at the source code, we can find that when preloading, ZygoteInit.preloadClasses() calls Class.forName(), which actually specifies BootClassLoader as the class loader, and initializes the BootClassLoader process only once. In short, through Class.forName() or
Class .classForName() can and can only load the basic class directly. Once the basic class is preloaded, although we cannot directly access BootClassLoader for the application, it can be loaded through Class.forName/Class.classForName, which means that it can be loaded through Class
. forName gets an instance of the basic class?

Look at the approximate process

insert image description here

From Zygote startup, to creating vm, initializing and accumulating dex files, zygoteinit preloads basic classes, and incubating each application Appp process to load basic classes and some related classes; whether it is the system class loader (PathClassLoader) or custom class
loading DexClassLoader, the topmost ancestor loader defaults to BootClassLoader, which, like JVM, ensures the type safety of basic classes

The loading of the class has basically come to an end, let's rest for five minutes... Let's look at the loading of the Class file

Thread{sleep(5_000)} …


4) Class file loading

1. Dynamic loading through the Class.forName() method
2. Dynamic loading through the ClassLoader.loadClass() method
Class loading is divided into 3 steps: 1. Load (Load), 2. Link (Link), 3. Initialize (Intialize )

insert image description here
Timing of class loading:

1. Implicit loading:
(1) Create an instance of a class, which is a new object
(2) Access a static variable of a certain class or interface, or assign a value to the static variable
(3) Call a static method of the class
(4 ) Reflection Class.forName("android.app.ActivityThread")
(5) Initialize a subclass of a class (the parent class of the subclass will be initialized first)
2. Explicit loading:
(1) Use LoadClass() to load
(2) Use forName() to load

There are two methods in explicit loading, and the two methods are slightly different.

(1) The ClassLoader.loadclasses method can load a class, but it will not trigger the initialization of the class, that is to say, it will not initialize the static variables and code blocks in the class (2) The Class.forName method will not only load a class
, It will also trigger the initialization phase of the class to initialize the static variables and code blocks of this class (the code block will be executed)


5)PathClassLoader

PathClassLoader is mainly used for system and app class loaders, where optimizedDirectory is null, and the default directory /data/dalvik-cache/ is used

PathClassLoader is used as the system class loader of the application, and it is also initialized when the Zygote process starts (the basic process is: ZygoteInit.main() -> ZygoteInit.forkSystemServer() -> ZygoteInit.handleSystemServerProcess() -> ZygoteInit.createPathClassLoader( ). Executed after preloading the basic class), so each APP process automatically carries a PathClassLoader after forking out of Zygote, which is usually used to load the .dex file in the apk

After each App process hatches from zygote, it automatically carries a pathClassLoader, which is usually used to load the .dex file in the apk

6)DexClassLoader

The class loader that can load classes from the jar or apk containing classes.dex can be used to perform dynamic loading, but it must be an app private writable directory to cache odex files. It can load apk or jar files that are not installed in the system, so Many hot fixes and plug-in solutions use DexClassLoader

public class 
DexClassLoader extends BaseDexClassLoader {
    
    
 
   public DexClassLoader(String dexPath, String optimizedDirectory,
            String librarySearchPath, ClassLoader parent) {
    
    
        super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);
    }
}

Summary:
Looking at the source code, we can find that both DexClassLoader and PathClassLoader inherit from BaseDexClassLoader. These two classes only provide their own constructors without additional implementation. Difference: DexClassLoader provides optimizedDirectory, while
PathClassLoader
does not. OptimizedDirectory is used to store odex The place of the file, so you can use DexClassLoader to achieve dynamic loading


7) The loading process of BaseDexClassLoader

Skip it for now (I don't quite understand it yet)


8) LoadClass() of ClassLoader

public abstract class ClassLoader {
    
    
 
    public Class<?> loadClass(String className) throws ClassNotFoundException {
    
    
        return loadClass(className, false);
    }
 
    protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
    
    
        //判断当前类加载器是否已经加载过指定类,若已加载则直接返回
        Class<?> clazz = findLoadedClass(className);
 
        if (clazz == null) {
    
    
            //如果没有加载过,则调用parent的类加载递归加载该类,若已加载则直接返回
            clazz = parent.loadClass(className, false);
 
            if (clazz == null) {
    
    
                //还没加载,则调用当前类加载器来加载
                clazz = findClass(className);
            }
        }
        return clazz;
    }
}

The loading process of this method is as follows:
(1) Determine whether the current class loader has loaded the specified class, and return directly if it has been loaded, otherwise continue to execute; (2
) Call the class loading of the parent to load the class recursively, and check whether it is loaded, If it has been loaded, it will return directly, otherwise it will continue to execute;
(3) Call the current class loader and load it through findClass.

9) ClassLoader's findLoadedClass() loading

protected final Class<?> findLoadedClass(String name) {
    
    
    ClassLoader loader;
    if (this == BootClassLoader.getInstance())
        loader = null;
    else
        loader = this;
    return VMClassLoader.findLoadedClass(loader, name);
}

In summary, as shown in the figure below

insert image description here

3. Summary of the article

After working for a long time, I still haven't fully figured it out. Today's brain cells are here. Happy New Year everyone

4. References

https://bbs.kanxue.com/thread-271538.htm

Guess you like

Origin blog.csdn.net/a_Chaon/article/details/128577763