美文网首页
Android类加载过程解析

Android类加载过程解析

作者: DroidMind | 来源:发表于2017-09-15 17:07 被阅读0次

    涉及源码(Android 4.4.2):
    /libcore/libart/src/main/java/java/lang/Class.java
    /dalvik/vm/native/java_lang_Class.cpp
    /dalvik/vm/native/InternalNative.cpp
    /dalvik/vm/oo/Class.cpp
    /libcore/libart/src/main/java/java/lang/Class.java
    /libcore/libart/src/main/java/java/lang/ClassLoader.java
    /libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
    /libcore/dalvik/src/main/java/dalvik/system/DexPathList.java
    /libcore/dalvik/src/main/java/dalvik/system/DexFile.java
    /dalvik/vm/native/dalvik_system_DexFile.cpp
    /dalvik/vm/oo/Class.cpp
    /dalvik/libdex/DexFile.cpp

    从Class.forName()函数看起。

    public static Class<?> forName(String className) throws ClassNotFoundException {
        return forName(className, true, VMStack.getCallingClassLoader());
    }
    

    可以看到默认使用的classLoader是调用该函数的类的classLoader。

    public static Class<?> forName(String className, boolean shouldInitialize,
            ClassLoader classLoader) throws ClassNotFoundException {
    
        if (classLoader == null) {
            classLoader = ClassLoader.getSystemClassLoader();
        }
    
        Class<?> result;
        try {
            result = classForName(className, shouldInitialize, classLoader);
        } 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;
    

    最终会调用到native方法

    /dalvik/vm/native/java_lang_Class.cpp

    static void Dalvik_java_lang_Class_classForName(const u4* args, JValue* pResult)
    {
        StringObject* nameObj = (StringObject*) args[0];
        bool initialize = (args[1] != 0);
        Object* loader = (Object*) args[2];
    
        RETURN_PTR(dvmFindClassByName(nameObj, loader, initialize));
    }
    

    /dalvik/vm/native/InternalNative.cpp

    ClassObject* dvmFindClassByName(StringObject* nameObj, Object* loader,
        bool doInit)
    {
        ClassObject* clazz = NULL;
        char* name = NULL;
        char* descriptor = NULL;
    
        if (nameObj == NULL) {
            dvmThrowNullPointerException("name == null");
            goto bail;
        }
        name = dvmCreateCstrFromString(nameObj);
    
        if (!dexIsValidClassName(name, true)) {
            ALOGW("dvmFindClassByName rejecting '%s'", name);
            dvmThrowClassNotFoundException(name);
            goto bail;
        }
    
        descriptor = dvmDotToDescriptor(name);
        if (descriptor == NULL) {
            goto bail;
        }
    
        if (doInit)
            clazz = dvmFindClass(descriptor, loader);
        else
            clazz = dvmFindClassNoInit(descriptor, loader);
    
        if (clazz == NULL) {
            LOGVV("FAIL: load %s (%d)", descriptor, doInit);
            Thread* self = dvmThreadSelf();
            Object* oldExcep = dvmGetException(self);
            dvmAddTrackedAlloc(oldExcep, self);     /* don't let this be GCed */
            dvmClearException(self);
            dvmThrowChainedClassNotFoundException(name, oldExcep);
            dvmReleaseTrackedAlloc(oldExcep, self);
        } else {
            LOGVV("GOOD: load %s (%d) --> %p ldr=%p",
                descriptor, doInit, clazz, clazz->classLoader);
        }
    
    bail:
        free(name);
        free(descriptor);
        return clazz;
    }
    

    重点关注dvmFindClassNoInit方法。

    /dalvik/vm/oo/Class.cpp

    ClassObject* dvmFindClassNoInit(const char* descriptor,
            Object* loader)
    {
        assert(descriptor != NULL);
        //assert(loader != NULL);
    
        LOGVV("FindClassNoInit '%s' %p", descriptor, loader);
    
        if (*descriptor == '[') {
            /*
             * Array class.  Find in table, generate if not found.
             */
            return dvmFindArrayClass(descriptor, loader);
        } else {
            /*
             * Regular class.  Find in table, load if not found.
             */
            if (loader != NULL) {
                return findClassFromLoaderNoInit(descriptor, loader);
            } else {
                return dvmFindSystemClassNoInit(descriptor);
            }
        }
    }
    

    进入到findClassFromLoaderNoInit方法

    static ClassObject* findClassFromLoaderNoInit(const char* descriptor,
        Object* loader)
    {
    
        Thread* self = dvmThreadSelf();
    
      
         // 1、首先进行查找是否以及进行了加载
        ClassObject* clazz = dvmLookupClass(descriptor, loader, false);
        if (clazz != NULL) {
            LOGVV("Already loaded: %s %p", descriptor, loader);
            return clazz;
        } else {
            LOGVV("Not already loaded: %s %p", descriptor, loader);
        }
    
        char* dotName = NULL;
        StringObject* nameObj = NULL;
    
        /* convert "Landroid/debug/Stuff;" to "android.debug.Stuff" */
        dotName = dvmDescriptorToDot(descriptor);
        if (dotName == NULL) {
            dvmThrowOutOfMemoryError(NULL);
            return NULL;
        }
        nameObj = dvmCreateStringFromCstr(dotName);
        if (nameObj == NULL) {
            assert(dvmCheckException(self));
            goto bail;
        }
    
        dvmMethodTraceClassPrepBegin();
    
       
        LOGVV("--- Invoking loadClass(%s, %p)", dotName, loader);
        {
            // 2、如果没有加载,这里调用java/lang/ClassLoader的loadClass函数来加载类
            const Method* loadClass =
                loader->clazz->vtable[gDvm.voffJavaLangClassLoader_loadClass];
            JValue result;
            dvmCallMethod(self, loadClass, loader, &result, nameObj);
            clazz = (ClassObject*) result.l;
    
            dvmMethodTraceClassPrepEnd();
            Object* excep = dvmGetException(self);
            if (excep != NULL) {
    #if DVM_SHOW_EXCEPTION >= 2
                ALOGD("NOTE: loadClass '%s' %p threw exception %s",
                     dotName, loader, excep->clazz->descriptor);
    #endif
                dvmAddTrackedAlloc(excep, self);
                dvmClearException(self);
                dvmThrowChainedNoClassDefFoundError(descriptor, excep);
                dvmReleaseTrackedAlloc(excep, self);
                clazz = NULL;
                goto bail;
            } else if (clazz == NULL) {
                ALOGW("ClassLoader returned NULL w/o exception pending");
                dvmThrowNullPointerException("ClassLoader returned null");
                goto bail;
            }
        }
    
        /* not adding clazz to tracked-alloc list, because it's a ClassObject */
    
        dvmAddInitiatingLoader(clazz, loader);
    
        LOGVV("--- Successfully loaded %s %p (thisldr=%p clazz=%p)",
            descriptor, clazz->classLoader, loader, clazz);
    
    bail:
        dvmReleaseTrackedAlloc((Object*)nameObj, NULL);
        free(dotName);
        return clazz;
    }
    

    1、首先调用dvmLookupClass方法在gDvm.loadedClasses中查找是否存在该类,加载过的类都会存放到gDvm.loadedClasses中。

    2、如果没有加载过。可以看到最终是调用java/lang/ClassLoader的loadClass函数来加载类,下面来看看ClassLoader的loadClass方法。

    /libcore/libart/src/main/java/java/lang/ClassLoader.java

    public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }
    
    protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
        // 查看该类是否以及加载
        Class<?> clazz = findLoadedClass(className);
        // 委托机制
        if (clazz == null) {
            ClassNotFoundException suppressed = null;
            try {
                clazz = parent.loadClass(className, false);
            } catch (ClassNotFoundException e) {
                suppressed = e;
            }
            // 最终如果没有找到该类,就自己来查找
            if (clazz == null) {
                try {
                    clazz = findClass(className);
                } catch (ClassNotFoundException e) {
                    e.addSuppressed(suppressed);
                    throw e;
                }
            }
        }
    
        return clazz;
    }
    

    查看BaseDexClassLoader的findClass方法。

    /libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
        Class c = pathList.findClass(name, suppressedExceptions);
        if (c == null) {
            ClassNotFoundException cnfe = new ClassNotFoundException("Didn't find class \"" + name + "\" on path: " + pathList);
            for (Throwable t : suppressedExceptions) {
                cnfe.addSuppressed(t);
            }
            throw cnfe;
        }
        return c;
    }
    

    /libcore/dalvik/src/main/java/dalvik/system/DexPathList.java

    public Class findClass(String name, List<Throwable> suppressed) {
        // 遍历dexElements数组
        for (Element element : dexElements) {
            DexFile dex = element.dexFile;
    
            if (dex != null) {
                Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);
                if (clazz != null) {
                    return clazz;
                }
            }
        }
        if (dexElementsSuppressedExceptions != null) {
            suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
        }
        return null;
    }
    

    /libcore/dalvik/src/main/java/dalvik/system/DexFile.java

    public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {
        return defineClass(name, loader, mCookie, suppressed);
    }
    
    private static Class defineClass(String name, ClassLoader loader, int cookie,
                                     List<Throwable> suppressed) {
        Class result = null;
        try {
            result = defineClassNative(name, loader, cookie);
        } catch (NoClassDefFoundError e) {
            if (suppressed != null) {
                suppressed.add(e);
            }
        } catch (ClassNotFoundException e) {
            if (suppressed != null) {
                suppressed.add(e);
            }
        }
        return result;
    }
    
    private static native Class defineClassNative(String name, ClassLoader loader, int cookie)
            throws ClassNotFoundException, NoClassDefFoundError;
    
    

    /dalvik/vm/native/dalvik_system_DexFile.cpp

    static void Dalvik_dalvik_system_DexFile_defineClassNative(const u4* args,
        JValue* pResult)
    {
        StringObject* nameObj = (StringObject*) args[0];
        Object* loader = (Object*) args[1];
        int cookie = args[2];
        ClassObject* clazz = NULL;
        DexOrJar* pDexOrJar = (DexOrJar*) cookie;
        DvmDex* pDvmDex;
        char* name;
        char* descriptor;
    
        name = dvmCreateCstrFromString(nameObj);
        descriptor = dvmDotToDescriptor(name);
        ALOGV("--- Explicit class load '%s' l=%p c=0x%08x",
            descriptor, loader, cookie);
        free(name);
    
        if (!validateCookie(cookie))
            RETURN_VOID();
    
        if (pDexOrJar->isDex)
            pDvmDex = dvmGetRawDexFileDex(pDexOrJar->pRawDexFile);
        else
            pDvmDex = dvmGetJarFileDex(pDexOrJar->pJarFile);
    
        /* once we load something, we can't unmap the storage */
        pDexOrJar->okayToFree = false;
    
        clazz = dvmDefineClass(pDvmDex, descriptor, loader);
        Thread* self = dvmThreadSelf();
        if (dvmCheckException(self)) {
            /*
             * If we threw a "class not found" exception, stifle it, since the
             * contract in the higher method says we simply return null if
             * the class is not found.
             */
            Object* excep = dvmGetException(self);
            if (strcmp(excep->clazz->descriptor,
                       "Ljava/lang/ClassNotFoundException;") == 0 ||
                strcmp(excep->clazz->descriptor,
                       "Ljava/lang/NoClassDefFoundError;") == 0)
            {
                dvmClearException(self);
            }
            clazz = NULL;
        }
    
        free(descriptor);
        RETURN_PTR(clazz);
    }
    

    重点看看dvmDefineClass方法

    /dalvik/vm/oo/Class.cpp

    ClassObject* dvmDefineClass(DvmDex* pDvmDex, const char* descriptor,
        Object* classLoader)
    {
        assert(pDvmDex != NULL);
    
        return findClassNoInit(descriptor, classLoader, pDvmDex);
    }
    
    
    static ClassObject* findClassNoInit(const char* descriptor, Object* loader,
        DvmDex* pDvmDex)
    {
        ClassObject* clazz;
        // 查看是否以及加载
        clazz = dvmLookupClass(descriptor, loader, true);
        // 如果没有加载,则进行查找加载
         if (clazz == NULL) {
             if (pDvmDex == NULL) {
                pDvmDex = searchBootPathForClass(descriptor, &pClassDef);
            } else {
                pClassDef = dexFindClass(pDvmDex->pDexFile, descriptor);
            }
            
            clazz = loadClassFromDex(pDvmDex, pClassDef, loader);
            // 进行缓存
            dvmAddClassToHash(clazz);
         }
         
         
    }
    

    下面看看dexFindClass方法。

    /dalvik/libdex/DexFile.cpp

    const DexClassDef* dexFindClass(const DexFile* pDexFile,
        const char* descriptor)
    {
        const DexClassLookup* pLookup = pDexFile->pClassLookup;
        u4 hash;
        int idx, mask;
    
        hash = classDescriptorHash(descriptor);
        mask = pLookup->numEntries - 1;
        idx = hash & mask;
    
        /*
         * Search until we find a matching entry or an empty slot.
         */
        while (true) {
            int offset;
    
            offset = pLookup->table[idx].classDescriptorOffset;
            if (offset == 0)
                return NULL;
    
            if (pLookup->table[idx].classDescriptorHash == hash) {
                const char* str;
    
                str = (const char*) (pDexFile->baseAddr + offset);
                if (strcmp(str, descriptor) == 0) {
                    return (const DexClassDef*)
                        (pDexFile->baseAddr + pLookup->table[idx].classDefOffset);
                }
            }
    
            idx = (idx + 1) & mask;
        }
    }
    

    从DexFile的pClassLookup中进行查找

    /dalvik/vm/oo/Class.cpp

    bool dvmAddClassToHash(ClassObject* clazz)
    {
        void* found;
        u4 hash;
    
        hash = dvmComputeUtf8Hash(clazz->descriptor);
    
        dvmHashTableLock(gDvm.loadedClasses);
        found = dvmHashTableLookup(gDvm.loadedClasses, hash, clazz,
                    hashcmpClassByClass, true);
        dvmHashTableUnlock(gDvm.loadedClasses);
    
        ALOGV("+++ dvmAddClassToHash '%s' %p (isnew=%d) --> %p",
            clazz->descriptor, clazz->classLoader,
            (found == (void*) clazz), clazz);
    
        //dvmCheckClassTablePerf();
    
        /* can happen if two threads load the same class simultaneously */
        return (found == (void*) clazz);
    }
    

    已经被加载过的类会存放到gDvm.loadedClasses里面去。

    整个流程图基本如下:

    dexload3.png

    相关文章

      网友评论

          本文标题:Android类加载过程解析

          本文链接:https://www.haomeiwen.com/subject/kdqesxtx.html