关于Java中几种loadClass的讨论

标签: java loadclass | 发表时间:2016-01-21 14:12 | 作者:weihe6666
出处:http://www.iteye.com

                                          关于Java中几种loadClass的讨论

java中有几类加载class的方法,本文针对这几个方法,就行源码导读。

 

本文的native源码来源于android4.1.2源码。

 

1. Class.forName(className, true, classLoader);
clazz = Class.forName(className, true, classLoader);
className:要加载的类的全路径名。

classLoader:类加载器。

这个函数会调用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));

}


这里调用的是dvmFindClassByName(nameObj, loader, initialize),重点在这句,由于此函数代码比较多,我只罗列了关键语句:


ClassObject* dvmFindClassByName(StringObject* nameObj, Object* loader,

    bool doInit)

{

    ClassObject* clazz = NULL;

    char* name = NULL;

    char* descriptor = NULL;

    ......

// 初始化的参数在这里体现

    if (doInit)

        clazz = dvmFindClass(descriptor, loader);

    else

        clazz = dvmFindClassNoInit(descriptor, loader);


    .......


}


由于dvmFindClass会先调用dvmFindClassNoInit,下面优先分析dvmFindClassNoInit

由于代码比较多,下面只列出了调用函数关系:

dvmFindClassNoInit-->findClassFromLoaderNoInit-->dvmLookupClass-->dvmHashTableLookup


关键看dvmHashTableLookup这个函数:


void* dvmHashTableLookup(HashTable* pHashTable, u4 itemHash, void* item,

   HashCompareFunc cmpFunc, bool doAdd)
{

    pEntry = &pHashTable->pEntries[itemHash & (pHashTable->tableSize-1)];
    pEnd = &pHashTable->pEntries[pHashTable->tableSize];
    while (pEntry->data != NULL) {
        if (pEntry->data != HASH_TOMBSTONE &&
            pEntry->hashValue == itemHash &&
            (*cmpFunc)(pEntry->data, item) == 0)  // 这里是需要ClassObject的关键点
    }

    return result;
}

此函数寻找ClassObject的逻辑是:

通过classname生成hash值,在HashTable中寻找此类的ClassObject,而这个HashTable保存在全局变量DvmGlobals中,ClassObject也就是C++中的对象,在虚拟机的堆中,classname和classloader两者对应唯一的一个ClassObject。看一下关键的比较函数cmpFunc如何判定此ClassObject是classname要寻找的Object

 

    match = (strcmp(clazz->descriptor, pCrit->descriptor) == 0 &&

             (clazz->classLoader == pCrit->loader ||

              (pCrit->loader != NULL &&

 

               dvmLoaderInInitiatingList(clazz, pCrit->loader)) ));

这里通过clazz->descriptor,classLoader 决定是否是要寻找的类对象。

 

所以有关java中ClassLoader内容,不同的类在不同的ClassLoader中加载,对应的不同的ClassObject,有此函数可以得出此结论。

 

回到刚才的函数,如果通过dvmLookupClass寻找不到对应的ClassObject,怎么处理?

会调用Invoke loadClass()进行class加载和初始化。

 

        const Method* loadClass =

            loader->clazz->vtable[gDvm.voffJavaLangClassLoader_loadClass];

        JValue result;

        dvmCallMethod(self, loadClass, loader, &result, nameObj);

        clazz = (ClassObject*) result.l;

 

这段函数还是比较重要的,通过ClassLoader,进行重新加载,dvmCallMethod是java调用native的入口。

 

 

2. ClassLoader Class<?> loadClass(String className, boolean resolve)

 

这个函数可能会被表面定义混淆,这个函数的逻辑也是先在HashTable中需要className对应的ClassObjet,如果寻找不到,在进行加载和初始化。

 

    protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {

        Class<?> clazz = findLoadedClass(className);

 

        if (clazz == null) {

            try {

                clazz = parent.loadClass(className, false);

            } catch (ClassNotFoundException e) {

                // Don't want to see this.

            }

 

            if (clazz == null) {

                clazz = findClass(className);

            }

        }

 

        return clazz;

    }

 

一、首先看Class<?> clazz = findLoadedClass(className);这句

 

对应的native源码在java_lang_VMClassLoader.cpp中。

 

static void Dalvik_java_lang_VMClassLoader_loadClass(const u4* args,

    JValue* pResult)

{

    StringObject* nameObj = (StringObject*) args[0];

    bool resolve = (args[1] != 0);

    ClassObject* clazz;

 

    clazz = dvmFindClassByName(nameObj, NULL, resolve);

    assert(clazz == NULL || dvmIsClassLinked(clazz));

    RETURN_PTR(clazz);

}

 

重点看一下dvmFindClassByName

 

ClassObject* dvmFindClassByName(StringObject* nameObj, Object* loader,

    bool doInit)

{

    ClassObject* clazz = NULL;

    char* name = NULL;

    char* descriptor = NULL;

    if (doInit)  // 这里是这个函数的重点逻辑

        clazz = dvmFindClass(descriptor, loader);

    else

        clazz = dvmFindClassNoInit(descriptor, loader);

}

 

这里如果查找不到,便会返回null。

 

二、clazz = parent.loadClass(className, false);

这里是调用classloader的父类进行加载,其加载逻辑相同。

 

三、 clazz = findClass(className);

如果一和二都不能找到className对应的ClassObject,那么就要通过findClass(className)进行寻找,这个函数的定义在classLoader,而android平台下定义在BaseDexClassLoader,看一下他的函数:

    protected Class<?> findClass(String name) throws ClassNotFoundException {

        Class clazz = pathList.findClass(name);

 

        if (clazz == null) {

            throw new ClassNotFoundException("Didn't find class \"" + name + "\" on path: " + originalPath);

        }

 

        return clazz;

    }

 

这里调用的是pathList.findClass(name);

 

继续跟踪函数:

    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;

 

    }

 

这里调用的是dex.loadClassBinaryName(name, definingContext);,对应的native函数为:

dalvik_system_DexFile.cpp

 

static void Dalvik_dalvik_system_DexFile_defineClass(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);

 

 

}

 

 clazz = dvmDefineClass(pDvmDex, descriptor, loader);这句是核心,

直接进入Class.cpp中的static ClassObject* findClassNoInit(const char* descriptor, Object* loader,

    DvmDex* pDvmDex)函数,这个函数的简单逻辑是dvmLookupClass寻找class,如果找不到,会创建对应的ClassObject,并且添加到Globals中。

 

 clazz = dvmLookupClass(descriptor, loader, true); // 寻找对应的ClassObject

    if (clazz == NULL) {

        const DexClassDef* pClassDef;

 

        dvmMethodTraceClassPrepBegin();

        profilerNotified = true;

 

#if LOG_CLASS_LOADING

        u8 startTime = dvmGetThreadCpuTimeNsec();

#endif

 

// 通过dex加载对应的类

        if (pDvmDex == NULL) {

            assert(loader == NULL);     /* shouldn't be here otherwise */

            pDvmDex = searchBootPathForClass(descriptor, &pClassDef);

        } else {

            pClassDef = dexFindClass(pDvmDex->pDexFile, descriptor);

        }

 

        if (pDvmDex == NULL || pClassDef == NULL) {

            if (gDvm.noClassDefFoundErrorObj != NULL) {

                /* usual case -- use prefabricated object */

                dvmSetException(self, gDvm.noClassDefFoundErrorObj);

            } else {

                /* dexopt case -- can't guarantee prefab (core.jar) */

                dvmThrowNoClassDefFoundError(descriptor);

            }

            goto bail;

        }

 

        /* found a match, try to load it */

        clazz = loadClassFromDex(pDvmDex, pClassDef, loader);

        if (dvmCheckException(self)) {

            /* class was found but had issues */

            if (clazz != NULL) {

                dvmFreeClassInnards(clazz);

                dvmReleaseTrackedAlloc((Object*) clazz, NULL);

            }

            goto bail;

        }

 

        /*

         * Lock the class while we link it so other threads must wait for us

         * to finish.  Set the "initThreadId" so we can identify recursive

         * invocation.  (Note all accesses to initThreadId here are

         * guarded by the class object's lock.)

         */

        dvmLockObject(self, (Object*) clazz);

        clazz->initThreadId = self->threadId;

 

        /*

         * Add to hash table so lookups succeed.

         *

         * [Are circular references possible when linking a class?]

         */

        assert(clazz->classLoader == loader);

        if (!dvmAddClassToHash(clazz)) { 添加创建的类到HashTable

            /*

             * Another thread must have loaded the class after we

             * started but before we finished.  Discard what we've

             * done and leave some hints for the GC.

             *

             * (Yes, this happens.)

             */

            //ALOGW("WOW: somebody loaded %s simultaneously", descriptor);

            clazz->initThreadId = 0;

            dvmUnlockObject(self, (Object*) clazz);

 

            /* Let the GC free the class.

             */

            dvmFreeClassInnards(clazz);

            dvmReleaseTrackedAlloc((Object*) clazz, NULL);

 

            /* Grab the winning class.

             */

            clazz = dvmLookupClass(descriptor, loader, true);

            assert(clazz != NULL);

            goto got_class;

        }

        dvmReleaseTrackedAlloc((Object*) clazz, NULL);

......

}

 

到此整个流程已经介绍完毕。

 

由此看出我们直接调用ClassLoader.loadClass要比调用Class.forName更高效。

 

 

 

 

 

 

 



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [java loadclass] 推荐:

关于Java中几种loadClass的讨论

- - 移动开发 - ITeye博客
                                          关于Java中几种loadClass的讨论. java中有几类加载class的方法,本文针对这几个方法,就行源码导读. 本文的native源码来源于android4.1.2源码. className:要加载的类的全路径名.

Java中的锁(Locks in Java)

- - 并发编程网 - ifeve.com
原文链接 作者:Jakob Jenkov 译者:申章 校对:丁一. 锁像synchronized同步块一样,是一种线程同步机制,但比Java中的synchronized同步块更复杂. 因为锁(以及其它更高级的线程同步机制)是由synchronized同步块的方式实现的,所以我们还不能完全摆脱synchronized关键字( 译者注:这说的是Java 5之前的情况).

Java PaaS 对决

- 呆瓜 - IBM developerWorks 中国 : 文档库
本文为 Java 开发人员比较了三种主要的 Platform as a Service (PaaS) 产品:Google App Engine for Java、Amazon Elastic Beanstalk 和 CloudBees RUN@Cloud. 它分析了每种服务独特的技术方法、优点以及缺点,而且还讨论了常见的解决方法.

Java浮点数

- d0ngd0ng - 译言-电脑/网络/数码科技
Thomas Wang, 2000年3月. Java浮点数的定义大体上遵守了二进制浮点运算标准(即IEEE 754标准). IEEE 754标准提供了浮点数无穷,负无穷,负零和非数字(Not a number,简称NaN)的定义. 在Java开发方面,这些东西经常被多数程序员混淆. 在本文中,我们将讨论计算这些特殊的浮点数相关的结果.

Qt——转战Java?

- - 博客 - 伯乐在线
编者按:事实上,在跨平台开发方面,Qt仍是最好的工具之一,无可厚非,但Qt目前没有得到任何主流移动操作系统的正式支持. 诺基亚的未来计划,定位非常模糊,这也是令很多第三方开发者感到失望,因此将导致诺基亚屡遭失败的原因. Qt的主要开发者之一Mirko Boehm在博客上强烈讽刺Nokia裁了Qt部门的决定,称其为“绝望之举”,而非“策略变更”.

java 验证码

- - ITeye博客
// 创建字体,字体的大小应该根据图片的高度来定. // 随机产生160条干扰线,使图象中的认证码不易被其它程序探测到. // randomCode用于保存随机产生的验证码,以便用户登录后进行验证. // 随机产生codeCount数字的验证码. // 得到随机产生的验证码数字. // 产生随机的颜色分量来构造颜色值,这样输出的每位数字的颜色值都将不同.

Java异常

- - CSDN博客推荐文章
“好的程序设计语言能够帮助程序员写出好程序,但是无论哪种语言都避免不了程序员写出坏的程序.                                                                                                                          ----《Java编程思想》.

java面试题

- - Java - 编程语言 - ITeye博客
 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面. 抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节. 抽象包括两个方面,一是过程抽象,二是数据抽象.  继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法. 对象的一个新类可以从现有的类中派生,这个过程称为类继承.

Java使用memcached

- - 互联网 - ITeye博客
首先到 http://danga.com/memcached下载memcached的windows版本和java客户端jar包,目前最新版本是memcached-1.2.1-win32.zip和java_memcached-release_1.6.zip,分别解压后即可. 然后是安装运行memcached服务器,我们将memcached-1.2.1-win32.zip解压后,进入其目录,然后运行如下命令:c:>;memcached.exe -d install
c:>memcached.exe -l 127.0.0.1 -m 32 -d start.

Java线程池

- - 企业架构 - ITeye博客
线程的使用在java中占有极其重要的地位,在jdk1.4极其之前的jdk版本中,关于线程池的使用是极其简陋的. 在jdk1.5之后这一情况有了很大的改观. Jdk1.5之后加入了java.util.concurrent包,这个包中主要介绍java中线程以及线程池的使用. 为我们在开发中处理线程的问题提供了非常大的帮助.