Java 类的C++描述

本文详细介绍了Java中类对象(ClassObject)的内存布局及其实现原理,包括类的加载过程、成员变量与方法的访问控制等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

类对像内存实现

java.lang.Class&ClassObject

在java语言里的特殊的类 java.lang.Class,它保存了类的信息。我们可以用它得到类的名字、成员、方法包括构造方法等等。

举个例子,我们写java.lang.String stringClass=Class.forName(“java.lang.String”);于是我们得到了java.lang.Class的一个实例,它存储的信息是关于java.lang.String这个类型的信息。注意不要和newInstance()搞起来,它是创建一个对象,而且forName的静态方法,newInstance是实例方法。

而在java虚拟机中,java.lang.Class 的一个实例就对应 ClassObject 这个类的一个对象。一般同一个类型在虚拟机中只存在一个 ClassObject,因为当程序需要某个类型的ClassObject的时候会到存放ClassObject的已加载的类型的哈希表中查找,有的话会直接返回,没有的话则创建一个ClassObject加入这个哈希表中。

同时,ClassObject还可以表示原始类型。boolean,char,float,double,byte,short,int,long,引用。这些类型都是虚拟机初始化的时候创建的。

先看一个结构:Object。源代码在

android2.2/platform/dalvik/vm/oo 下

Toggle line numbers
   1 typedef struct Object {
   2     ClassObject*    clazz;      //类型对象
   3     Lock           lock;        /*锁对象,只要是实例对象都有对应的锁,某个线程获得对它的锁以后,如果其它线程要获得它的锁,只有等这个线程释放了它的锁,才能真正获得锁。*/
   4 } Object;
   5  
   6 /*类的状态,随着进展而改变*/
   7 typedef enum ClassStatus {
   8     CLASS_ERROR             = -1,       /*出错了*/
   9     CLASS_NOTREADY      = 0,        /*刚开始,什么也没做*/
  10     CLASS_LOADED            = 1,        /*已加载*/
  11     CLASS_PREPARED          = 2,        /*已准备,连接的一部分*/
  12     CLASS_RESOLVED          = 3,        /*已解析,连接的一部分*/
  13     CLASS_VERIFYING         = 4,        /*正在验证,逻辑上属于连接的一部分*/
  14     CLASS_VERIFIED          = 5,        /*验证完毕,逻辑上属于连接的一部分,准备初始化*/
  15     CLASS_INITIALIZING      = 6,        /*初始化中*/
  16     CLASS_INITIALIZED       = 7,        /*初始化好了,全好了,可以用了*/
  17 } ClassStatus;

这是Dalvik虚拟机类型生命周期开始的图解。

Toggle line numbers
   1 typedef enum PrimitiveType {
   2     PRIM_NOT            = -1,
   3     PRIM_BOOLEAN        = 0,
   4     PRIM_CHAR           = 1,
   5     PRIM_FLOAT          = 2,
   6     PRIM_DOUBLE         = 3,
   7     PRIM_BYTE           = 4,
   8     PRIM_SHORT          = 5,
   9     PRIM_INT            = 6,
  10     PRIM_LONG           = 7,
  11     PRIM_VOID           = 8,
  12     PRIM_MAX
  13 } PrimitiveType;

关于类的方法的调用:

  1. 如果是一个接口的引用调用方法就一定是invokeinterface。否则2
  2. 如果是调用静态方法就一定是invokestatic。否则3
  3. 如果是super关键字调用方法,不管是super(…)还是super….(…)就一定是调用invokespecial。否则4
  4. 如果是调用<init>方法就一定是invokespecial。否则5

  5. 如果是调用私有方法就一定是调用invokespecial。否则6
  6. 余下的都是调用invokervirtual。

Toggle line numbers
   1 /*用于ClassObject的iftable列表中*/
   2 typedef struct InterfaceEntry {
   3     ClassObject*        clazz;  /*表示接口的ClassObject对象*/
   4     /*指向在vtable中对应方法的位置偏移的索引数组ifviPool中的位置偏移的索引数组(详见图)*/
   5     int*                methodIndexArray;
   6 } InterfaceEntry;
   7  
   8 struct Method {
   9     /*方法所属的类型*/
  10     ClassObject*        clazz;
  11     /*方法的访问标志,可以为ACC_PUBLIC、ACC_PRIVATE、ACC_PROTECTED、ACC_STATIC、ACC_FINAL、ACC_SYNCHRONIZED、ACC_BRIDGE、ACC_VARARGS、ACC_NATIVE、ACC_ABSTRACT、ACC_STRICT、ACC_SYNTHETIC、ACC_CONSTRUCTOR和ACC_DECLARED_SYNCHRONIZED及其组合 */
  12     u4              accessFlags;
  13     /*对于虚方法,这个是方法在虚方法表中的偏移;对于接口表的入口这是方法的偏移即iftable[n]->methodIndexArray*/
  14     u2             methodIndex;
  15     /*对于本地方法,我们通过参数列表来计算大小,最初始的时候令insSize和registerSize都等于这个结果*/
  16     u2              registersSize;  /* ins + locals */
  17     u2              outsSize;
  18     u2              insSize;
  19     /*方法名,比如"<init>" 或 "eatLunch" */
  20     const char*         name;
  21     /*方法原型描述字符串*/
  22     DexProto        prototype;
  23     /*方法描述字符串的缩短形式*/
  24     const char*         shorty;
  25     /*剩下的变量不被用于抽象和本地方法中(JNI认为”insns”是一个函数指针,在第一次被调用之后设置。对于内部的本地方法,这是NULL。) */
  26     /*实际代码所在的首地址*/
  27     const u2*           insns;          /*指令,通过内存映像的.dex文件*/
  28     /*缓冲的JNI参数和返回类型提示*/
  29     int                 jniArgInfo;
  30     /*本地方法指针。可以是真正的函数也可以是JNI桥。当前我们并不区分DalvikBridgeFunc和DalvikNativeFunc,前者的参数是个超集(有额外的两个参数,它们会被忽略)。必要的话我们可以使用insns==NULL来判断是JNI桥还是内部本地函数*/
  31     DalvikBridgeFunc nativeFunc;
  32 };
  33  
  34 struct Field {
  35     ClassObject*        clazz;         /*所属的类型*/
  36     const char*         name;         /*变量的名称*/
  37     const char*         signature;      /*变量的签名比如"I", "[C", "Landroid/os/Debug;"等等*/
  38     u4              accessFlags;      /*访问标志,可以是ACC_PUBLIC、ACC_PRIVATE、ACC_PROTECTED、ACC_STATIC、ACC_FINAL、ACC_VOLATILE、ACC_TRANSIENT、ACC_SYNTHETIC和ACC_ENUM及其组合*/
  39 };
  40  
  41 struct StaticField {
  42     Field               field;           /*必须是第一项*/
  43     JValue              value;          /*对于原始类型直接由DEX设置*/
  44 };
  45  
  46 struct InstField {
  47     Field               field;           /*必须是第一项*/
  48     /*从Object*地址处开始的偏移位置*/
  49     int                 byteOffset;
  50 };
  51  
  52 struct DataObject {
  53     Object          obj;                /*必须是第一项*/
  54     /* u4类型项的变量数;u8使用两项*/
  55     u4              instanceData[1];
  56 };
  57 struct StringObject {
  58     Object          obj;                /*必须是第一项*/
  59     /* u4类型项的变量数;u8使用两项*/
  60     u4              instanceData[1];
  61 };
  62 struct ArrayObject {
  63     Object          obj;                /*必须是第一项*/
  64     /*元素个数,初始化后不会改变*/
  65     u4              length;
  66     /*数组的内容,大小为length * sizeof(type)。总长度必须是8字节对齐的,实际分配的大小可能小于8字节*/
  67     u8              contents[1];
  68 };

下面让我们看看ClassObject是怎样的。

Toggle line numbers
   1 struct ClassObject {
   2     /*必须放在第一个,你可以看到后面的几个结构,如DataObject、StringObject和ArrayObject都是这样安排为的是获得实例数据时有统一的偏移便于实例数据的获取*/
   3     Object          obj;
   4     /*为实例数据流出4字节的空间*/
   5     u4              instanceData[CLASS_FIELD_SLOTS];
   6     /*UTF-8的描述字符串*/
   7 const char*         descriptor;
   8 /*另一个描述字符串,现在发现好像在反射机制的代理类用到*/
   9     char*          descriptorAlloc;
  10     /*访问标志,对于外部类而言,可以是ACC_PUBLIC、ACC_FINAL、ACC_INTERFACE、ACC_ABSTRACT、ACC_SYNTHETIC、ACC_ANNOTATION和ACC_ENUM的组合;对于内部类而言,可以是ACC_CLASS_MASK、ACC_PRIVATE、ACC_PROTECTED和ACC_STATIC的组合*/
  11     u4              accessFlags;
  12     /* 指向对应的DexFile当从常量池中查询信息时要用到,如果是虚拟机自己生成的类,比如数组和原始类等等则为空*/
  13     DvmDex*        pDvmDex;
  14     /*类的状态,按顺序可以是CLASS_NOTREADY、CLASS_LOADED 、CLASS_PREPARED、CLASS_RESOLVED、CLASS_VERIFYING、CLASS_VERIFIED      、CLASS_INITIALIZING、CLASS_INITIALIZED以及CLASS_ERROR*/
  15     ClassStatus         status;
  16     /*类验证失败时抛出的异常的类型,如果以后又访问这个类则可以直接获得这个类型*/
  17     ClassObject*        verifyErrorClass;
  18     /*初始化时的线程id,用于在<clinit>发生嵌套调用时作检查*/
  19     u4              initThreadId;
  20     /*这个类型所对应的对象的大小,用于在堆上分配内存时使用。如果是接口或抽象类,这个值就是0*/
  21     size_t         objectSize;
  22     /*数组元素的类型,仅当这个类型为数组类型时有效。用于instanceof操作符或强制类型转换时使用,比如String[][][]类型的这个值就是String类型*/
  23     ClassObject*        elementClass;
  24     /*这个类型对应的数组类型,当这个类型的数组类型被首次用到时才被创建和赋值*/
  25     ClassObject*        arrayClass;
  26     /*数组的维数,仅当这个类型为数组时才有效,比如int[][]的值为2 */
  27     int                 arrayDim;
  28     /*原始类型的下标,用于虚拟机生成的原始类型,非原始类型时为PRIM_NOT (-1) */
  29     PrimitiveType       primitiveType;
  30     /*超类的类型,如果是java.lang.Object的话这个值为NULL */
  31     ClassObject*        super;
  32     /*这个类的定义加载器,如果类型为“bootstrap”的系统类加载器则为NULL[微软用户1] */
  33     Object*             classLoader;
  34     /*需要初始化这个类的加载器的列表,即这个类的初始化加载器的列表*/
  35 Object**            initiatingLoaders;
  36 /*这个类的初始化加载器数*/
  37     int                 initiatingLoaderCount;
  38     /*本类直接实现的接口数*/
  39 int                 interfaceCount;
  40 /*本类直接实现的接口列表*/
  41     ClassObject**       interfaces;
  42     /*所谓的direct方法即static,private和<init>方法数*/
  43 int                 directMethodCount;
  44 /*directr方法列表*/
  45     Method*        directMethods;
  46     /*本类定义的虚方法数*/
  47     int                 virtualMethodCount;
  48 /*本类定义的虚方法,所谓虚方法就是通过虚方法表vtable来调用的方法*/
  49 Method*        virtualMethods;
  50     /*虚方法表中的方法数*/
  51     int                 vtableCount;
  52     /*虚方法表,通过invokevirtual来调用。首先从超类完全复制过来虚表,然后我们再部分得替换它或者扩展它*/
  53     Method**       vtable;
  54     /*类实现的接口数*/
  55     int                 iftableCount;
  56     /*类的接口表,每个接口一个表项。不管是由类直接实现的接口,还是由超类间接实现的接口。如果一个接口都未实现那么这个表就为NULL*/
  57     InterfaceEntry*     iftable;
  58 /*在vtable中对应方法的位置偏移的索引数组中的元素个数*/
  59    int                 ifviPoolCount;
  60 /*指向在vtable中对应方法的位置偏移的索引数组*/
  61     int*                ifviPool;
  62     /*静态变量个数*/
  63 int                 sfieldCount;
  64 /*静态变量的数组*/
  65     StaticField*        sfields;
  66     /*实例变量的个数*/
  67 int                 ifieldCount;
  68 /*实例变量中引用的个数*/
  69 int                 ifieldRefCount;
  70 /*实例变量数组*/
  71 InstField*          ifields;
  72     /*源文件的文件名*/
  73     const char*         sourceFile;
  74 };
  75  
  76 bool dvmClassStartup(void)
  77 {
  78     ClassObject* unlinkedClass; //供加载和连接时假的引用类对象
  79     ……
  80     gDvm.loadedClasses =
  81         dvmHashTableCreate(256, (HashFreeFunc) dvmFreeClassInnards);    //供加载类的哈希表
  82   ……
  83     unlinkedClass = &gDvm.unlinkedJavaLangClassObject;
  84     memset(unlinkedClass, 0, sizeof(*unlinkedClass));
  85     DVM_OBJECT_INIT(&unlinkedClass->obj, NULL);
  86     unlinkedClass->descriptor = "!unlinkedClass";
  87     gDvm.unlinkedJavaLangClass = unlinkedClass;
  88     assert(gDvm.bootClassPath == NULL);
  89     processClassPath(gDvm.bootClassPathStr, true);
  90 ……
  91     return true;
  92 }
  93  
  94 void dvmClassShutdown(void)
  95 {
  96     int i;
  97     dvmHashTableFree(gDvm.loadedClasses);
  98     gDvm.loadedClasses = NULL;
  99     for (i = 0; i < PRIM_MAX; i++)
 100         dvmFreeClassInnards(gDvm.primitiveClass[i]);
 101     freeCpeArray(gDvm.bootClassPath);
 102     gDvm.bootClassPath = NULL;
 103     dvmLinearAllocDestroy(NULL);
 104 }

findClassNoInit:

dvmLinkClass: 解析超类à解析每个接口àcreateVtableàcreateIftable

访问控制: 关于成员变量和成员方法的访问控制

引自:http://blog.youkuaiyun.com/bazookier/archive/2009/08/09/4427404.aspx

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值