.class文件

目标定位与运行环境

面向通用 Java 虚拟机(JVM),由 javac 编译生成,每个 .class 文件对应一个 Java 类(或接口)

执行模型

JVM 是基于栈的虚拟机,只能通过操作栈访问操作数

文件结构

https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html

.dex

Android Runtime字节码文件,可以由d8等工具将若干 .class 转换打包而成。一个 APK 中通常只有一个或多个 classes.dex,内部包含所有类。运行前可进一步编译为 OAT/ODEX文件。

执行模型

dalvik虚拟机是基于寄存器的虚拟机。与class基于操作数栈的模型不一样。这里的寄存器是虚拟寄存器,与物理寄存器的概念不一样,在每个方法的描述中有这个方法会用到的虚拟寄存器的个数。

寄存器

寄存器机器减少内存访问、执行跳转更直接。

当用于位值(例如整数和浮点数)时,寄存器会被视为宽度为 32 位。如果值是 64 位,则使用两个相邻的寄存器。对于寄存器对,没有对齐要求。

当用于对象引用时,寄存器会被视为其宽度正好能够容纳一个此类引用。

指令

Class文件的指令以一个字节为一个单位(一个字节的操作码后面根操作数) Dex文件的指令以2个字节为一个单位(操作码也是一个字节,但是操作数的分布比较复杂)

帧结构

帧的大小在创建时确定后就固定不变。每一帧由特定数量的寄存器(由方法指定)以及执行该方法所需的所有辅助数据构成。

解释器模式

runtime/interpreter/shadow_frame.h 定义了 ShadowFrame,并且解释器入口函数 EnterInterpreterFromEntryPoint 就会接收一个 ShadowFrame* 参数来执行字节码。

  ShadowFrame* link_; // 上一个帧
  ArtMethod* method_; // 当前方法数据结构ArtMethod的指针
  LockCountData lock_count_data_;  // This may contain GC roots when lock counting is active.
  const uint32_t number_of_vregs_; // 寄存器个数

  uint32_t dex_pc_; 

  // This is a set of ShadowFrame::FrameFlags which denote special states this frame is in.
  // NB alignment requires that this field takes 4 bytes no matter its size. Only 7 bits are
  // currently used.
  uint32_t frame_flags_;

  // This is a two-part array:
  //  - [0..number_of_vregs) holds the raw virtual registers, and each element here is always 4
  //    bytes.
  //  - [number_of_vregs..number_of_vregs*2) holds only reference registers. Each element here is
  //    ptr-sized.
  // In other words when a primitive is stored in vX, the second (reference) part of the array will
  // be null. When a reference is stored in vX, the second (reference) part of the array will be a
  // copy of vX.
  uint32_t vregs_[0];

Quick (AOT/JIT) 模式下

方法调用会走quick stubs和本地栈帧,并不分配或使用 ShadowFrame 对象。

在 runtime/entrypoints/quick、runtime/art_method.cc 中通过 OatQuickMethodHeader, callee-save 信息,以及平台原生栈布局来完成,完全靠机器栈和寄存器存储上下文,不涉及ShadowFrame。

只有在deopt时,ART 才会将这些 Quick 帧转成 Interpreter 模式的 ShadowFrame,以便继续在解释器里执行或单步调试。

文件结构

https://source.android.google.cn/docs/core/runtime/dex-format

dex文件有header,header中有string\type\proto\field\method\class\data字段的大小和偏移 主要区别在于因为一个dex文件包含很多类,所以字符串去重效果会更好,使用LEB128变长编码提高整型存储效率,然后方法会有比class文件更短的shorty的描述符,总的来说存储效率更高。