http://blog.csdn.net/zhangyun438/article/details/17192787 内容如下:

Java 源代码经过编译后会生成后缀为class的文件,也即字节码文件。然后在Android中使用dx工具将其转换为后缀为jar 的dex类型文件。Dalvik 虚拟机负责解释并执行编译后的字节码。在解释执行字节码之前,当然要读取文件,分析文件的内容,得到字节码,然后才能解释执行之。在整个的加载过程中,最为重要的就是对Class的加载 – Class包含Method,Method 又包含code。通过对Class的加载,我们即可获得所需执行的字节码。

本文从dexfile 文件分析及Class加载中的数据结构入手,结合主要流程,对整个加载过程进行分析,期望对大家有所帮助。

1. DexFile 在内存中的映射

android 系统中,java源文件会被编译为后缀为jar的dex类型文件,在代码中称为dexfile。在加载Class之前,必先读取相应的jar文件。通常我们使用read函数来读取文件中的内容。但在Dalvik中使用mmap函数,和read不同,mmap函数会将dex文件映射到内存中,这样通过普通的内存读取操作即可访问dex file 中的内容。

Dexfile 的文件格式如下图所示,主要有三部分组成:头部,索引,数据。通过头部可知索引的位置和数目,可知数据区的起始位置。其中classDefsOff指定了ClassDef在文件的起始位置,dataOff指定了数据在文件的起始位置,ClassDef即可理解为Class的索引。通过读取ClassDef可获知Class的基本信息,其中classDataOff指定了Class数据在数据区的位置。

在将dexfile文件映射到内存后,即会调用dexFileParse函数对其分析,分析的结果存放于名为DexFile 的数据结构中。DexFile中的baseAddr指向映射区的起始位置,pClassDefs指向ClassDefs(即class索引)的起始位置。由于在查找class时,都是使用class的名字进行查找,为了加快查找速度,创建了一个hash表。在hash表中对class名字进行hash,并生成index。这些操作都是在对文件解析时所完成的,这样虽然在加载过程中比较耗时,但是在运行过程中确可节省大量查找时间。

2. ClassObject -  Class在加载后的表现形式

在对文件解析完成后就要加载Class的具体内容了!在Dalvik中,由ClassObject 这个数据结构负责存放加载的信息。如下图所示,加载过程会在内存中alloc几个区域,分别存放directMethods, virtualMethods, sfields, ifields。这些信息正是从dex 文件的数据区中读取。首先会读取Class的详细信息,从中获知directMethod, virtualMethod, sfield, ifield等的信息,然后再读取。下图为加载完成后的示意。 这里并未介绍加载的每个细节,感兴趣的同学可通过此二图自行分析。

还请大家注意的是在ClassObject结构中有个名为super的成员。通过super成员来指向它的超类。

3. findClassNoInit – 负责加载Class并生成相应ClassObject的函数。

第二节介绍了加载后的数据结构,本节会分析负责加载工作的函数- findClassNoInit。 请注意在获取Class索引时,会分为基本类库文件和用户类文件两种情况。在Dalvik分析之准备篇中,grund.sh中有一语句   “export BOOTCLASSPATH=$bootpath/core.jar:$bootpath/ext.jar:$bootpath/framework.jar:$bootpath/android.police.jar” 。 这条语句指定了Dalvik所需的基本库文件,如果没有此语句,Dalvik在启动过程中就会报错退出。

LoadClassFromDex 函数先会读取Class的具体数据(从ClassDataoff处),然后按图索骥,分别加载 directMethod, virtualMethod,ifield,sfield。

作为业界TOP公司出品的东东,当然要关注执行效率了^_^。首先,在加载后我们要将其缓存起来,以便以后使用方便。其次,在查找过程中,如果我们顺序查找的话,当然是很慢的拉。这当然是俺们senior engineer所不能容忍的,所以gDvm.loadedClasses这个Hash表就隆重出场了。什么,这位同学说没有几个Class,至于这么大动干戈么。让我们看看,通过在准备篇中介绍的方法,我们在main.c 249行设置断点,这个时候基本库已加载完毕。当程序停下时,我们看看gDvm的值,可以看到numLoadedClasses这个成员的值为212 !也即意味着这个时候我们什么也没做,用户类也未加载,Dalvik 已加载的Class数目已达到212。

dvmLinkClass, 好长,但是其最终好像会再次调用findClassNoInit。嗯,也是可以理解的嘛。如果一个子类需要调用超类的函数,那它当然要先加载超类了,可能的话,甚至会加载超类的超类^_^。

眼见为虚,实践出真知,拿gdb调试下。

在findClassNoInit函数处设置断点(在gdb提示符后输入”b findClassNoInit”),在gdb提示符后连续几次执行”c”和”bt”。可出现如下信息,可以看到在函数调用栈上可以多次看到findClassNoInit的身影。

(gdb) bt

#0  findClassNoInit (descriptor=0xfef4c7f4 "??????%", loader=0x0, pDvmDex=0x0)

at dalvik/vm/oo/Class.c:1373

#1  0xf6fc4d53 in dvmFindClassNoInit (descriptor=0xf5046a63 "Ljava/lang/Object;", loader=0x0)

at dalvik/vm/oo/Class.c:1194

#2  0xf6fc6c0a in dvmResolveClass (referrer=0xf5837400, classIdx=290,

fromUnverifiedConstant=false) at dalvik/vm/oo/Resolve.c:94

#3  0xf6fc3476 in dvmLinkClass (clazz=0xf5837400, classesResolved=false)

at dalvik/vm/oo/Class.c:2537

#4  0xf6fc1b67 in findClassNoInit (descriptor=0xf6ff0df6 "Ljava/lang/Class;", loader=0x0,

pDvmDex=0xa04c720) at dalvik/vm/oo/Class.c:1489

现在从另一个角度去观察。在class.c 2575行设置断点,然后等待程序停下。看下clazz的内容。

(gdb) p clazz->super->descriptor

$6 = 0xf5046a63 "Ljava/lang/Object;"

(gdb) p clazz->descriptor

$7 = 0xf5046121 "Ljava/lang/Class;"

4. 基本类库文件的加载

先在findClassNoInit函数处设置断点,然后运行程序,等待程序的停下。

(gdb) b findClassNoInit

Breakpoint 2 at 0xf6fc13e0: file dalvik/vm/oo/Class.c, line 1373.

(gdb) c

Continuing.

看看谁是第一个加载的Class,及其调用关系。

(gdb) bt

#0  findClassNoInit (descriptor=0x0, loader=0x0, pDvmDex=0x0) at dalvik/vm/oo/Class.c:1373

#1  0xf6fc32a1 in dvmLinkClass (clazz=0xf5837350, classesResolved=false)

at dalvik/vm/oo/Class.c:2491

#2  0xf6fc1b67 in findClassNoInit (descriptor=0xf6ff1ded "Ljava/lang/Thread;", loader=0x0,

pDvmDex=0xa04c720) at dalvik/vm/oo/Class.c:1489

#3  0xf6f92692 in dvmThreadObjStartup () at dalvik/vm/Thread.c:328

#4  0xf6f800e6 in dvmStartup (argc=2, argv=0xa041190, ignoreUnrecognized=false, pEnv=0xa0411a0)

at dalvik/vm/Init.c:1155

#5  0xf6f8b8e3 in JNI_CreateJavaVM (p_vm=0xf6ff0df6, p_env=0xf6ff0df6, vm_args=0xfef4d0b0)

at dalvik/vm/Jni.c:4198

#6  0x08048893 in main (argc=3, argv=0xfef4d168) at dalvik/dalvikvm/Main.c:212

函数调用顺序清晰可见:main -> JNI_CreateJavaVM-> dvmStartup-> dvmThreadObjStartup-> dvmFindSystemClassNoInit-> findClassNoInit 观察仔细的同学可能会问在调用栈中没有看到dvmFindSystemClassNoInit啊,为何你这样写啊?我估计编译器将其作为inline优化了,导致gdb看不到有dvmFindSystemClassNoInit的栈。从回溯栈中也可清晰的看到

5. 用户类文件的加载

用户类文件的加载颇为曲折,它会先加载一个Class。然后这个Class去负责用户类文件的加载,当然了,这个Class又会通过JNI的方式去曲折调用到findClassNoInit。这个就留给大家自己分析了, 其实楼主自己也不是很明白为什么要这么大费周折^_^

[zhuan]Dalvik 分析 - Class加载篇的更多相关文章

  1. <JVM中篇:字节码与类的加载篇>04-再谈类的加载器

    笔记来源:尚硅谷JVM全套教程,百万播放,全网巅峰(宋红康详解java虚拟机) 同步更新:https://gitee.com/vectorx/NOTE_JVM https://codechina.cs ...

  2. <JVM中篇:字节码与类的加载篇>03-类的加载过程(类的生命周期)详解

    笔记来源:尚硅谷JVM全套教程,百万播放,全网巅峰(宋红康详解java虚拟机) 同步更新:https://gitee.com/vectorx/NOTE_JVM https://codechina.cs ...

  3. <JVM中篇:字节码与类的加载篇>02-字节码指令集

    笔记来源:尚硅谷JVM全套教程,百万播放,全网巅峰(宋红康详解java虚拟机) 同步更新:https://gitee.com/vectorx/NOTE_JVM https://codechina.cs ...

  4. <JVM中篇:字节码与类的加载篇>01-Class字节码文件结构

    笔记来源:尚硅谷JVM全套教程,百万播放,全网巅峰(宋红康详解java虚拟机) 同步更新:https://gitee.com/vectorx/NOTE_JVM https://codechina.cs ...

  5. Lab1:练习四——分析bootloader加载ELF格式的OS的过程

    练习四:分析bootloader加载ELF格式的OS的过程. 1.题目要求 通过阅读bootmain.c,了解bootloader如何加载ELF文件.通过分析源代码和通过qemu来运行并调试bootl ...

  6. 1.Sentinel源码分析—FlowRuleManager加载规则做了什么?

    最近我很好奇在RPC中限流熔断降级要怎么做,hystrix已经1年多没有更新了,感觉要被遗弃的感觉,那么我就把眼光聚焦到了阿里的Sentinel,顺便学习一下阿里的源代码. 这一章我主要讲的是Flow ...

  7. mybatis源码分析--如何加载配置及初始化

    简介 Mybatis 是一个持久层框架,它对 JDBC 进行了高级封装,使我们的代码中不会出现任何的 JDBC 代码,另外,它还通过 xml 或注解的方式将 sql 从 DAO/Repository ...

  8. 精尽Spring Boot源码分析 - 配置加载

    该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...

  9. 第一次源码分析: 图片加载框架Picasso源码分析

    使用: Picasso.with(this) .load("http://imgstore.cdn.sogou.com/app/a/100540002/467502.jpg") . ...

随机推荐

  1. linux下的java开发环境

    一.jdk的安装 1.复制jdk至安装目录,我们指定的安装目录是:/usr/local/java .可是系统安装后在/usr/local下并没有java目录,这需要我们去创建一个java文件夹,如图

  2. Windows10安装GPU版本的Tensorflow

    本人电脑配置(公司的)gtx1080ti,下载的的cuda8.0,cudnn6.0,python3.5.3安装完成后,安装tensorflow 1.pip install tensorflow-gpu ...

  3. CsvHelper文档-2读

    CsvHelper文档-2读 这个库默认不需要做任何设置就可以很容易的使用它.如果你的类属性名称直接匹配csv的标题名称,那么可以按照下面的实例来用: (以下所有的代码都需要引用using csvhe ...

  4. [Clr via C#读书笔记]Cp18 定制Attribute

    Cp18 定制Attribute 意义 利用Attribute,可以声明性的给自己的代码结构创建注解,从而实现一些特殊的功能:最终在元数据中生成,这种可扩展的元数据信息可以在运行时的时候查询,从而动态 ...

  5. 【shell 练习4】编写Shell用户管理脚本(二)

    一.创建.删除.查看用户,随机生成八位数密码 #!/bin/bash #Author:yanglt #!/bin/bash #Author:yanglt #Blog:https://www.cnblo ...

  6. 关于excle导数据的一些代码笔记

    package com.bonc.util; import java.io.File; import java.io.FileInputStream; import java.io.FileOutpu ...

  7. 购物单:Excel的应用

    题目描述: 小明刚刚找到工作,老板人很好,只是老板夫人很爱购物.老板忙的时候经常让小明帮忙到商场代为购物.小明很厌烦,但又不好推辞. 这不,XX大促销又来了!老板夫人开出了长长的购物单,都是有打折优惠 ...

  8. 今日头条 2018 AI Camp 6 月 2 日在线笔试编程题第一道——最大连续区间和扩展

    题目 给出一个长度为 n 的数组a1.a2.....ana1.a2.....an,请找出在所有连续区间 中,区间和最大同时这个区间 0 的个数小于等于 3 个,输出这个区间和. 输入描述: 第一行一个 ...

  9. Python3 数据类型-列表

    序列是Python中最基本的数据结构.序列中的每个元素都分配一个数字 - 它的位置,或索引,第一个索引是0,第二个索引是1,依此类推. 索引如下图: 列表命名(list): 组成:使用[]括起来,并且 ...

  10. Calculator PartⅢ

    GitHub/object-oriented The title of the work 这次敲代码耗时相对较短,但是始终无法完成debug步骤,目前上传的代码可以通过编译,但运行即报停,问题调试为内 ...