内存碎片问题其实是一个通用的问题,不单止Dalvik虚拟机在Java堆为对象分配内存时会遇到,C库的malloc函数在分配内存时也会遇到。Android系统使用的C库bionic使用了Doug Lea写的dlmalloc内存分配器。也就是说,我们调用函数malloc的时候,使用的是dlmalloc内存分配器来分配内存。这是一个成熟的内存分配器,可以很好地解决内存碎片问题。关于dlmalloc内存分配器的设计,可以参考这篇文章:A Memory Allocator。
前面Dalvik虚拟机垃圾收集机制简要介绍和学习计划一文提到,Dalvik虚拟机的Java堆的底层实现是一块匿名共享内存,并且将其抽象为C库的一个mspace,如图1所示:
图1 Dalvik虚拟机Java堆
于是,Dalvik虚拟机就很机智地利用C库里面的dlmalloc内存分配器来解决内存碎片问题!
为了应对可能面临的内存不足问题,Dalvik虚拟机采用一种渐进的方法来为对象分配内存,直到尽了最大努力,如图2所示:
图2 Dalvik虚拟机为对象分配内存的过程
接下来,我们就详细分析这个过程,以便可以了解Dalvik虚拟机是如何解决内存不足问题的,以及分配出来的内存是如何管理的。
Dalvik虚拟机实现了一个dvmAllocObject函数。每当Dalvik虚拟机需要为对象分配内存时,就会调用函数dvmAllocObject。例如,当Dalvik虚拟机的解释器遇到一个new指令时,它就会调用函数dvmAllocObject,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
HANDLE_OPCODE(OP_NEW_INSTANCE /*vAA, class@BBBB*/) { ClassObject* clazz; Object* newObj; EXPORT_PC(); vdst = INST_AA(inst); ref = FETCH(1); ...... clazz = dvmDexGetResolvedClass(methodClassDex, ref); if (clazz == NULL) { clazz = dvmResolveClass(curMethod->clazz, ref, false); ...... } ...... newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK); ...... SET_REGISTER(vdst, (u4) newObj); } FINISH(2); OP_END |
这个代码段定义在文件dalvik/vm/mterp/out/InterpC-portable.cpp中。
关于Dalvik虚拟机的解释器的实现,可以参考Dalvik虚拟机的运行过程分析一文,上面这段代码首先是找到要创建的对象的类型clazz,接着以其作为参数调用函数dvmAllocObject为要创建的对象分配内存。另外一个参数ALLOC_DONT_TRACK是告诉Dalvik虚拟机的堆管理器,要分配的对象是一个根集对象,不需要对它进行跟踪。因为根集对象在GC时是会自动被追踪处理的。
函数dvmAllocObject的实现如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Object* dvmAllocObject(ClassObject* clazz, int flags) { Object* newObj; assert(clazz != NULL); assert(dvmIsClassInitialized(clazz) || dvmIsClassInitializing(clazz)); /* allocate on GC heap; memory is zeroed out */ newObj = (Object*)dvmMalloc(clazz->objectSize, flags); if (newObj != NULL) { DVM_OBJECT_INIT(newObj, clazz); dvmTrackAllocation(clazz, clazz->objectSize); /* notify DDMS */ } return newObj; } |
这个函数定义在文件dalvik/vm/alloc/Alloc.cpp中。
函数dvmAllocObject调用函数dvmMalloc从Java堆中分配一块指定大小的内存给新创建的对象使用。如果分配成功,那么接下来就先使用宏DVM_OBJECT_INIT来初始化新创建对对象的成员变量clazz,使得新创建的对象可以与某个特定的类关联起来,接着再调用函数dvmTrackAllocation记录当前的内存分配信息,以便通知DDMS。
函数dvmMalloc返回的只是一块内存地址,这是没有类型的。但是由于每一个Java对象都是从Object类继承下来的,因此,函数dvmAllocObject可以将获得的没有类型的内存块强制转换为一个Object对象。
Object类的定义如下所示:
1 2 3 4 5 6 7 8 9 10 |
struct Object { /* ptr to class object */ ClassObject* clazz; /* * A word containing either a "thin" lock or a "fat" monitor. See * the comments in Sync.c for a description of its layout. */ u4 lock; }; |
这个类定义在文件dalvik/vm/oo/Object.h中。
Object类有两个成员变量:clazz和lock。其中,成员变量clazz的类型为ClassObject,它对应于Java层的java.lang.Class类,用来描述对象所属的类。成员变量lock是一个锁,正是因为有了这个成员变量,在Java层中,每一个对象都可以当锁使用。
理解了Object类的定义之后,我们继续分析函数dvmMalloc的实现,如下所示: