Dalvik虚拟机Java堆创建过程分析

Dalvik虚拟机Java堆创建过程分析 

 

 

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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值