static void LinkCode(SirtRef<mirror::ArtMethod>& method, const OatFile::OatClass* oat_class,
uint32_t method_index)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// Method shouldn't have already been linked.
DCHECK(method->GetEntryPointFromCompiledCode() == NULL);
// Every kind of method should at least get an invoke stub from the oat_method.
// non-abstract methods also get their code pointers.
const OatFile::OatMethod oat_method = oat_class->GetOatMethod(method_index);
oat_method.LinkMethod(method.get());
// Install entry point from interpreter.
Runtime* runtime = Runtime::Current();
bool enter_interpreter = NeedsInterpreter(method.get(), method->GetEntryPointFromCompiledCode());
if (enter_interpreter) {
method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge);
} else {
method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
}
if (method->IsAbstract()) {
method->SetEntryPointFromCompiledCode(GetCompiledCodeToInterpreterBridge());
return;
}
if (method->IsStatic() && !method->IsConstructor()) {
// For static methods excluding the class initializer, install the trampoline.
// It will be replaced by the proper entry point by ClassLinker::FixupStaticTrampolines
// after initializing class (see ClassLinker::InitializeClass method).
method->SetEntryPointFromCompiledCode(GetResolutionTrampoline(runtime->GetClassLinker()));
} else if (enter_interpreter) {
// Set entry point from compiled code if there's no code or in interpreter only mode.
method->SetEntryPointFromCompiledCode(GetCompiledCodeToInterpreterBridge());
}
if (method->IsNative()) {
// Unregistering restores the dlsym lookup stub.
method->UnregisterNative(Thread::Current());
}
// Allow instrumentation its chance to hijack code.
runtime->GetInstrumentation()->UpdateMethodsCode(method.get(),
method->GetEntryPointFromCompiledCode());
}
ClassLinker
EnsureInitialized
Excute
extern "C" const void* artQuickResolutionTrampoline(mirror::ArtMethod* called,
mirror::Object* receiver,
Thread* thread, mirror::ArtMethod** sp)
mirror::ArtMethod** sp
invoke-static/invoke-direct/invoke-interface/invoke-super/invoke-virtual
static direct/invoke-interface/invoke-super/invoke-virtual
GetOatCodeFor ClassLinker
ClassLinker ::RegisterDexFileLocked
ObjectArray<ArtMethod> * reasons
我们提到,ART运行时的Image空间创建完成之后,会调用ImageSpace类的成员函数Init来对它进行初始化。
ImageSpace类的成员函数Init的实现如下所示:
Resolution Method本质上就一个ArtMethod对象。当我们调用dex2oat工具将系统启动类翻译成本地机器指令时,会创建这个Resolution Method,并且将它保存在Image文件中。这
DefineClass LoadMethod
1. 在生成的本地机器指令中,一个类方法调用另外一个类方法并不是直接进行的,而是通过Dex Cache来间接进行的。
2. 通过Dex Cache间接调用类方法,可以做到延时解析类方法,也就是等到类方法第一次被调用时才解析,这样可以避免解析那些永远不会被调用的类方法。
3. 一个类方法只会被解析一次,解析的结果保存在Dex Cache中,因此当该类方法再次被调用时,就可以直接从Dex Cache中获得所需要的信息。
以上就是Dex Cache在ART运行时所起到的作用了,理解这一点对阅读ART运行时的源代码非常重
ManagerStack
我们将继续发力,分析听起来很高大上的ART运行时垃圾收集机制
ART 垃圾回收机制
Dalvik虚拟机的垃圾收集过程。
Dalvik虚拟机的垃圾收集过程。
Active堆 heap Zygote Heap
Mark Sweep算法
Card tail-call
每一个对象的地址都有32位,除去最低的三位,我们在考虑Bitmap与对象的对应关系时,只需要考虑高29位就可以了。因此,在计算所需要的Bitmap的大小时,就可以将堆的大小值除以HB_OBJECT_ALIGNMENT,即除以8。
32 >> 8 29位
11111111111111111111111111111000
00011111111111111111111111111111
2^HB_BITS_PER_WORD
假设一个unsigned long数占HB_BITS_PER_WORD个位,那么,我们就需要一个大小为(Max Heap Size / HB_OBJECT_ALIGNMENT / HB_BITS_PER_WORD)的unsigned long数组来描述一个大小为Max Heap Size的堆。在32位设备上,一个unsigned long数占用32位,即HB_BITS_PER_WORD的值等于32。综合上面的描述,我们就可以知道,一个大小为Max Heap Size的堆需要一个大小为(Max Heap Size / 8 / 32)的unsigned long数组来描述,即需要一块字节数等于(Max Heap Size / 8 / 32)× 4的内存来描述。
在图1中,我们使用了两个Bitmap来描述堆的对象,一个称为Live Bitmap,另一个称为Mark Bitmap。Live Bitmap用来标记上一次GC时被引用的对象,也就是没有被回收的对象,而Mark Bitmap用来标记当前GC有被引用的对象。有了这两个信息之后,我们就可以很容易地知道哪些对象是需要被回收的,即在Live Bitmap在标记为1,但是在Mark Bitmap中标记为0的对象。
Live Bitmap Mark Bitmap GC
Concurrent GC Active堆 Max Heap Size
在Card Table中,在连续GC_CARD_SIZE地址中的对象共用一个Card。Dalvik虚拟机将GC_CARD_SIZE的值设置为128。因此,假设堆的大小为Max Heap Size,那么我们只需要一块字节数为(Max Heap Size / 128)的Card Table。相比大小为(Max Heap Size / 8 / 32)× 4的Bitmap,减少了一半的内存需求。
Mark Stack
Active堆 Zygote 堆
Card Table
Mark Stack
Live Heap
Mark heap
trimHeaps addNewHeap
一个字节就是一个字符 eg:'a'
Zygote Active堆
dvmHeapBitmapInit allocMarkStack
HeapBitmap
bool dvmHeapBitmapInit(HeapBitmap *hb, const void *base, size_t maxSize,
const char *name)
{
void *bits;
size_t bitsLen;
assert(hb != NULL);
assert(name != NULL);
bitsLen = HB_OFFSET_TO_INDEX(maxSize) * sizeof(*hb->bits);
bits = dvmAllocRegion(bitsLen, PROT_READ | PROT_WRITE, name);
if (bits == NULL) {
ALOGE("Could not mmap %zd-byte ashmem region '%s'", bitsLen, name);
return false;
}
hb->bits = (unsigned long *)bits;
hb->bitsLen = hb->allocLen = bitsLen;
hb->base = (uintptr_t)base;
hb->max = hb->base - 1;
return true;
}
代码对HeapBitmap结构体的各个成员变量的含义已经有很详细的注释,其中最重要的就是成员变量bits指向的一个类型为unsigned long的数组,这个数组的每一个bit都用来标记一个对象是否存活。
回到函数dvmHeapBitmapInit中,Java堆的起始地址为base,大小为maxSize,由此我们就知道,在Java堆上创建的对象的地址范围为[base, maxSize)。但是通过C库提供的mspace_malloc来在Java堆分配内存时,得到的内存地址是以8字节对齐的。这意味着我们只需要(maxSize / 8)个bit来描述Java堆的对象。结构体HeapBitmap的成员变量bits是一个类型为unsigned long的数组,也就是说,数组中的每一个元素都可以描述sizeof(unsigned long)个对象的存活。在32位设备上,一个unsigned long占用32个bit,这意味着需要一个大小为(maxSize / 8 / 32)的unsigned long数组来描述Java堆对象的存活。如果换成字节数来描述的话,就是说我们需要一块大小为(maxSize / 8 / 32) × 4的内存块来描述一个大小为maxSize的Java堆对象。
Dalvik虚拟机提供了一些宏来描述对象地址与HeapBitmap结构体的成员变量bits所描述的unsigned long数组的关系,如下所示:
base / maxSize
[base, maxSize) = [0xFFFFFFF0,]
maxSize / 8 / 32
GcMarkStack
#define HB_OBJECT_ALIGNMENT 8
#define HB_BITS_PER_WORD (sizeof(unsigned long) * CHAR_BIT)
/* <offset> is the difference from .base to a pointer address.
* <index> is the index of .bits that contains the bit representing
* <offset>.
*/
#define HB_OFFSET_TO_INDEX(offset_) \
((uintptr_t)(offset_) / HB_OBJECT_ALIGNMENT / HB_BITS_PER_WORD)
offset / 8 * (4 * 8)
(maxSize / 8 / 32)的unsigned long数组 (maxSize / 8 / 32)个 32bit即,32(bit)个,32(bit)个,32(bit)个
回到函数dvmHeapBitmapInit中,Java堆的起始地址为base,大小为maxSize,由此我们就知道,在Java堆上创建的对象的地址范围为[base, maxSize)。但是通过C库提供的mspace_malloc来在Java堆分配内存时,得到的内存地址是以8字节对齐的。这意味着我们只需要(maxSize / 8)个bit来描述Java堆的对象。结构体HeapBitmap的成员变量bits是一个类型为unsigned long的数组,也就是说,数组中的每一个元素都可以描述sizeof(unsigned long)个对象的存活。在32位设备上,一个unsigned long占用32个bit,这意味着需要一个大小为(maxSize / 8 / 32)的unsigned long数组来描述Java堆对象的存活。如果换成字节数来描述的话,就是说我们需要一块大小为(maxSize / 8 / 32) × 4的内存块来描述一个大小为maxSize的Java堆对象。
#define HB_INDEX_TO_OFFSET(index_) \
((uintptr_t)(index_) * HB_OBJECT_ALIGNMENT * HB_BITS_PER_WORD)
index_ * 8 * (4 * 8) =
#define HB_OFFSET_TO_BYTE_INDEX(offset_) \
(HB_OFFSET_TO_INDEX(offset_) * sizeof(*((HeapBitmap *)0)->bits))
(maxSize / 8 / 32)的unsigned long数组 (maxSize / 8 / 32)个 32bit即,32(bit)个,32(bit)个,32(bit)个
bit换算成字节后 4(byte)个,4(byte)个,4(byte)个,
(32bit)1个,(32bit)1个,(32bit)1个 -》4(byte)4个,4(byte)4个,4(byte)4个,
回到函数dvmHeapBitmapInit中,Java堆的起始地址为base,大小为maxSize,由此我们就知道,在Java堆上创建的对象的地址范围为[base, maxSize)。
但是通过C库提供的mspace_malloc来在Java堆分配内存时,得到的内存地址是以8字节对齐的。
这意味着我们只需要(maxSize / 8)个bit来描述Java堆的对象
。结构体HeapBitmap的成员变量bits是一个类型为unsigned long的数组,也就是说,数组中的每一个元素都可以描述sizeof(unsigned long)个对象的存活。在32位设备上,一个unsigned long占用32个bit,这意味着需要一个大小为(maxSize / 8 / 32)的unsigned long数组来描述Java堆对象的存活。如果换成字节数来描述的话,就是说我们需要一块大小为(maxSize / 8 / 32) × 4的内存块来描述一个大小为maxSize的Java堆对象。
假设我们知道了一个对象的地址为ptr,Java堆的起始地址为base,那么就可以计算得到一个偏移值offset。有了这个偏移值之后,就可以通过宏HB_OFFSET_TO_INDEX计算得到用来描述该对象存活的bit位于HeapBitmap结构体的成员变量bits所描述的unsigned long数组的索引index。有了这个index之后,我们就可以得到一个unsigned long值。接着再通过对象地址ptr的第4到第8位表示的数值为索引,在前面找到的unsigned long值取出相应的位,就可以得到该对象是否存活了。
就是说我们需要一块大小为(maxSize / 8 / 32) × 4的内存块来描述一个大小为maxSize的Java堆对象。
#define HB_OFFSET_TO_BYTE_INDEX(offset_) \
(HB_OFFSET_TO_INDEX(offset_) * sizeof(*()))
4-8 unsigned long
struct GcMarkStack {
/* Highest address (exclusive)
*/
const Object **limit;
/* Current top of the stack (exclusive)
*/
const Object **top;
/* Lowest address (inclusive)
*/
const Object **base;
/* Maximum stack size, in bytes.
*/
size_t length;//字节长度
};
GcMarkStack
一个大小为maximumSize的Java堆,在最坏情况下,存在(maximumSize / (sizeof(Object) + HEAP_SOURCE_CHUNK_OVERHEAD))个对象。
在最坏情况下,存在(maximumSize / (sizeof(Object) + HEAP_SOURCE_CHUNK_OVERHEAD))个对象。也就是说,GcMarkStack通过一个大小为(maximumSize / (sizeof(Object) + HEAP_SOURCE_CHUNK_OVERHEAD))的Object*数组来描述一个栈。如果换成字节数来描述的话,就是说我们需要一块大小为(maximumSize * sizeof(Object*) / (sizeof(Object) + HEAP_SOURCE_CHUNK_OVERHEAD))的内存块来描述一个GcMarkStack栈。
stack->length = maximumSize * sizeof(Object*) /
(sizeof(Object) + HEAP_SOURCE_CHUNK_OVERHEAD);
GC_CARD_SHIFT 7
GC_DIRTY_CARD GC_CARD_CLEAN
gDvm.biasedCardTableBase
return card
cardTableBase cardTableOffset
CardTable
在Dalvik虚拟机中,Card Table和Heap Bitmap的作用是类似的。区别在于:
1. Card Table不是使用一个bit来描述一个对象,而是用一个byte来描述GC_CARD_SIZE个对象;
2. Card Table不是用来描述对象的存活,而是用来描述在Concurrent GC的过程中被修改的对象,这些对象需要进行特殊处理。
dvmAddrFromCard
offset << GC_CARD_SHIFT gDvm.biasedCardTableBase
CardTable 字节地址
Mark Heap Bitmap Live Heap Bitmap
Mark Stack Card Table
在Dalvik虚拟机中,Card Table和Heap Bitmap的作用是类似的。区别在于:
1. Card Table不是使用一个bit来描述一个对象,
而是用一个byte来描述GC_CARD_SIZE个对象;
bit - 1
byte -128
2. Card Table不是用来描述对象的存活,而是用来描述在Concurrent GC的过程中被修改的对象,这些对象需要进行特殊处理。
/*
* Maintain a card table from the the write barrier. All writes of
* non-NULL values to heap addresses should go through an entry in
* WriteBarrier, and from there to here.
*
* The heap is divided into "cards" of GC_CARD_SIZE bytes, as
* determined by GC_CARD_SHIFT. The card table contains one byte of
* data per card, to be used by the GC. The value of the byte will be
* one of GC_CARD_CLEAN or GC_CARD_DIRTY.
*
* After any store of a non-NULL object pointer into a heap object,
* code is obliged to mark the card dirty. The setters in
* ObjectInlines.h [such as dvmSetFieldObject] do this for you. The
* JIT and fast interpreters also contain code to mark cards as dirty.
*
* The card table's base [the "biased card table"] gets set to a
* rather strange value. In order to keep the JIT from having to
* fabricate or load GC_DIRTY_CARD to store into the card table,
* biased base is within the mmap allocation at a point where it's low
* byte is equal to GC_DIRTY_CARD. See dvmCardTableStartup for details.
*/
/*
* Initializes the card table; must be called before any other
* dvmCardTable*() functions.
*/
bool dvmCardTableStartup(size_t heapMaximumSize, size_t growthLimit)
{
size_t length;//字节
void *allocBase;
u1 *biasedBase;
GcHeap *gcHeap = gDvm.gcHeap;
int offset;
void *heapBase = dvmHeapSourceGetBase();
assert(gcHeap != NULL);
assert(heapBase != NULL);
/* All zeros is the correct initial value; all clean. */
assert(GC_CARD_CLEAN == 0);
/* Set up the card table */
length = heapMaximumSize / GC_CARD_SIZE;//字节数
heapMaximumSize 1 bit 1个
GC_CARD_SIZE 1byte -128个
/* Allocate an extra 256 bytes to allow fixed low-byte of base */
allocBase = dvmAllocRegion(length + 0x100, PROT_READ | PROT_WRITE,
"dalvik-card-table");
if (allocBase == NULL) {
return false;
}
gcHeap->cardTableBase = (u1*)allocBase;
gcHeap->cardTableLength = growthLimit / GC_CARD_SIZE;
gcHeap->cardTableMaxLength = length;
biasedBase = (u1 *)((uintptr_t)allocBase -
((uintptr_t)heapBase >> GC_CARD_SHIFT));
offset = GC_CARD_DIRTY - ((uintptr_t)biasedBase & 0xff);
gcHeap->cardTableOffset = offset + (offset < 0 ? 0x100 : 0);
biasedBase += gcHeap->cardTableOffset;
assert(((uintptr_t)biasedBase & 0xff) == GC_CARD_DIRTY);
gDvm.biasedCardTableBase = biasedBase;
return true;
}