`
kerrysk
  • 浏览: 16900 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

JVM基本原理--dalvik

阅读更多

一、简介

dalvik是google在其android智能手机操作系统中用的java虚拟机。借此讲一下我对虚拟机的基本理解吧。一切编程语言要想在计算机上运行必须翻译成机器码(这是废话)。java是一种半编译半解释型语言。半编译是指:java源代码,会经过javac命令变成 .class文件。半解释是指: .class文件被jvm解释的过程。也就是因为jvm的半解释才有了java的动态语言特性:反射和annotation。

 

二、类文件预处理

在半编译阶段,java源代码被编译,在.class文件中会有类信息和虚拟机指令。dalvik有自己的libdex库负责对.class进行处理。libdex主要对.class进行处理生成自己的dex文件。主要做的工作是,对虚拟机指令进行转换(dalvik是基于寄存器的,sun虚拟机是基于栈的),对类的静态数据进行归类、压缩。

 

三、类加载

在虚拟机启动时,根据输入的参数(一般有入口类(main函数所在的类)和jar包的路径),在classath路径中加载入口类,类加载过程是:虚拟机根据入口类的全称,去遍历classpath下的dex文件(dalvik第一次加载后会生成cache文件,供下次快速加载,所以第一次会很慢),获取入口类的信息,构建入口类的Class结构体,Class结构主要包含类的field、method、(anntation 和内部类dalvik放在native处理,没在Class结构体,这样为了节省内存)。另外在加载入口类之前,可能要加载,他所依赖的类,比如父类、Class类等,类被加载后一般会在classloader中保存,下次用可以直接取到。

  • filed主要信息,filed的类型、变量名对static fiedl 还有可能有初始值(非static field在构造函数中赋初值,不保证每个类都有静态初始化块<clinit>方法,所以staitc filed初始值要单独处理)。
  • method 主要有:方法签名、method Code(虚拟机指令)、exception等信息
四、类的初始化及resolve
在一个类的metod code被虚拟机解释器执行之前,一般要进行类的初始化,类初始化主要做:static变量的赋初值(如上所说)、及method code的预处理,因为在method code内都是静态的信息,比如:你要调一个类的某个方法,指令中可能只有类名和方法名的字符串信息,而不是这个这个method结构体的指针,如果要掉的类没有初始化,你可能还要初始化这个类。以及异常错误的处理,比如你调的方法不存在,要抛NoSuchMethodError。这步叫resolve。resolve过程也可以在解释器内做,因为你有可能初始化一个类,而不调用它的方法。dalvik是在第一次调一个方法时resolve的。

五、类的解释执行
在加载完入口类时,虚拟机会调用JNI(对虚拟机对外暴露的函数的封装,可以让java和C互相调用)的方法去执行,入口类的main方法,解释器首先获取此类的method结构体,获得method code一条一条解释执行。
常见的指令:
  • const、const-string等:一般是把一个常量(int double short boolean char string ……),放入一个寄存器。
  • add sub div xor or and ……:加减乘除、与或非等
  • getfield putfield 、getstatic putstaic:在堆上获取相应对象的field值,对static可能在Class结构体内存放
  • invokeXXX:调用某个方法,要在栈上保存当前frame信息(包括IP 及各寄存器值),push一个frame去解释另一个方法。方法调用是,要将这个方法的参数copy到新的frame 寄存器上。在被调用的方法结束时,在栈上pop出该frame,把方法返回值copy到调用frame的寄存器上。(这个可以有多种实现,dalvik是把返回值copy到全局的ret变量中,在用move_result指令来做到这一点)
  • return :告诉解释器方法调用结束
  • if if_eqz等:逻辑判断、还有swith case等也有专门指令
  • new指令,new一个对象放在堆上
六、异常处理
首先明确一个概念,就是一个java的线程在dalvik内对应一个C线程,每个java线程都会有自己的执行栈。Exception的处理时,当解释器遇到Excepiton时,把这个异常对象放入Thread结构体保存,在解释某些有可能抛异常的指令时,去check,check到,就结束执行下一条指令,往下找catch,如果没有就一直往上抛,只到栈上没frame,虚拟机结束。

七、堆结构及GC
堆是虚拟机为了预存对象,而事先分配的一块大内存。对象在堆上一般要保存,其Class信息(属于某个class)和对象每个field的值。GC时,虚拟机遍历所有线程栈及全局变量(JNI全局reference、Stringpool等),对有引用对象进行mark,然后遍历堆进行swap。另外还牵扯finalize的执行及java reference机制等。
堆设计最大的麻烦在于,频繁的分配和释放对象可能导致内存碎片。一般做法是在GC后要进行一次堆整理,但对整理会改变对象的指针地址,可能导致其他对象引用一个无效的指针,所以一般要另外为每个对象对应一个定长的map,在引用对象上保存一个不变的映射值,在根据这个值找到对象。堆整理时,改变引用值。这种做法的坏处是浪费了内存,对JNI的getArrayElements方法不得不采用copy模式等。另一种做法是不进行堆整理,这样要用好的算法控制不至于产生过多的内存碎片。



 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics