kvm启动流程-002

本文介绍初始化内存系统的代码.该方法定义如下:

void InitializeMemoryManagement(void)
{
    int index;
    gcInProgress = 0;// 是否gc在执行过程中
    InitializeHeap(); // 1.初始化堆

    // 2.初始化root
    index = 0;
    GlobalRoots[index++].cellpp = (cell **)&AllThreads;
    GlobalRoots[index++].cellpp = (cell **)&CleanupRoots;
    GlobalRootsLength = index;
    TemporaryRootsLength = 0;

    // 3.为CleanupRoots 分配空间
    CleanupRoots =
          (POINTERLIST)callocObject(SIZEOF_POINTERLIST(CLEANUP_ROOT_SIZE),// CLEANUP_ROOT_SIZE = 16
                                    GCT_POINTERLIST);
}

该方法有3步:

  1. 初始化堆
  2. 初始化root
  3. 为CleanupRoots 分配空间

其中第1步代码如下:

void InitializeHeap(void)
{
    VMHeapSize = RequestedHeapSize;
    // 1. 进行堆的初始化,如果初始化失败,则退出
    AllHeapStart = allocateHeap(&VMHeapSize, &TheHeap);

    if (TheHeap == NIL) {
        fatalVMError(KVM_MSG_NOT_ENOUGH_MEMORY);
    }

    /* 2. 初始化指针*/
    CurrentHeap    = AllHeapStart;
    CurrentHeapEnd = PTR_OFFSET(AllHeapStart, VMHeapSize);

#if !CHUNKY_HEAP
    FirstFreeChunk = (CHUNK)CurrentHeap;
    FirstFreeChunk->size =
           (CurrentHeapEnd -CurrentHeap - HEADERSIZE) << TYPEBITS;
    FirstFreeChunk->next = NULL;
#endif

    /*
     * 永久代的内存空间是从PermanentSpaceFreePtr 到CurrentHeapEnd.初始化的时候,其大小为0
     */
#if ENABLE_HEAP_COMPACTION
    PermanentSpaceFreePtr = CurrentHeapEnd;
#endif
    AllHeapEnd            = CurrentHeapEnd;

#if INCLUDEDEBUGCODE
    if (tracememoryallocation) {
        Log->allocateHeap(VMHeapSize, (long)AllHeapStart, (long)AllHeapEnd);
    }
    NoAllocation = 0;
#endif
}

步骤有2:

  1. 进行堆的初始化,如果初始化失败,则退出
  2. 初始化指针

第一步

第一步进行堆的初始化,如果初始化失败,则退出.调用的方法为allocateHeap,其代码如下:

cell *allocateHeap(long *sizeptr, void **realresultptr) { 
    // 1. 进行分配
    void *space = malloc(*sizeptr + sizeof(cell) - 1);
    *realresultptr = space;
    // 2. 进行内存对齐
    return (void *) ((((long)space) + (sizeof(cell) - 1)) & ~(sizeof(cell) - 1));
}

这里有2种情况,分别是分配的地址是4字节对齐,分配的地址不是4字节对齐.
如果是第2种情况,则需要一个指针来记录realresultptr实际分配的内存地址,这就是realresultptr的由来.


第二步

这部分的代码比较简单,可以知道在KVM内部定义了5个指针来记录内存的使用情况.分别是:

  • CurrentHeap --> 整个堆的起始地址
  • CurrentHeapEnd --> 堆的终点地址,整个堆分2部分,一部分是堆,一部分是持久代
  • FirstFreeChunk --> 链表结构,用来记录堆中的空闲内存
  • PermanentSpaceFreePtr --> 永久代的内存空间是从PermanentSpaceFreePtr 到CurrentHeapEnd.初始化的时候,其大小为0(即PermanentSpaceFreePtr 指向CurrentHeapEnd).
  • AllHeapEnd --> 整个堆的终点地址.

结合目前,可以得出如下两图:

  1. 初始化时,分配的地址是4字节对齐的.
    在这里插入图片描述

  2. 初始化时,分配的地址不是4字节对齐的.
    在这里插入图片描述


分配对象

在InitializeMemoryManagement中的第3步调用了callocObject分配对象.其代码如下:

cell* callocObject(long size, GCT_ObjectType type)
{
    // 1. 分配内存
    cell* result = mallocHeapObject(size, type);
    if (result == NULL) {
        THROW(OutOfMemoryObject);
    }

    /* 2. 初始化变量为0*/
    memset(result, 0, size << log2CELL);

    return result;
}

其中mallocHeapObject,代码如下:

cell* mallocHeapObject(long size, GCT_ObjectType type)
/* Remember: size is given in CELLs rather than bytes */
{
    cell* thisChunk;
    long realSize;

    //  为1的目的是,即使size为0,也需要1cell来存放对象头
    if (size == 0) size = 1;
    realSize = size + HEADERSIZE;

#if INCLUDEDEBUGCODE
    if (NoAllocation > 0) {
        fatalError(KVM_MSG_CALLED_ALLOCATOR_WHEN_FORBIDDEN);
    }
#endif /* INCLUDEDEBUGCODE */

    // EXCESSIVE_GARBAGE_COLLECTION 是为了除错的目的而存在的,如果设为1的话,则会在分配前进行垃圾收集的操作
    if (EXCESSIVE_GARBAGE_COLLECTION) {
        garbageCollect(0);
    }

    // 分配指定大小的内存空间
    thisChunk = allocateFreeChunk(realSize);
    if (thisChunk == NULL) {
    	// 如果分配失败的话,则进行垃圾收集后,再次进行分配,如果分配失败的话,则返回null
        garbageCollect(realSize); /* So it knows what we need */
        thisChunk = allocateFreeChunk(realSize);
        if (thisChunk == NULL) {
            return NULL;
        }
    }

    /* Augment the object header with gc type information */
    /* NOTE: This operation does not set the size of the object!! */
    /* You must initialize the size elsewhere, or otherwise the */
    /* memory system will be corrupted! */
    /*
     * 按照type参数来设定新配置的object header中type字段的值.
     */
    *thisChunk |= (type << TYPE_SHIFT);

#if INCLUDEDEBUGCODE
    if (tracememoryallocation) {
        Log->allocateObject((long)thisChunk,
                            (long)size+HEADERSIZE,
                            (int)type,
                            (long)thisChunk, memoryFree());
    }
#endif /* INCLUDEDEBUGCODE */

#if ENABLEPROFILING
    DynamicObjectCounter     += 1;
    DynamicAllocationCounter += (size+HEADERSIZE)*CELL;
#endif /* ENABLEPROFILING */

    return thisChunk + HEADERSIZE;
}

这里的注释还是比较多的,就不做过多介绍.

总结一下:在进行内存分配时,会多分配1字节用作对象头,而返回的时候是返回对象实际使用的地址(即thisChunk + HEADERSIZE).

这里比较重要的是allocateFreeChunk方法.其方法如下:

static cell* allocateFreeChunk(long size)
{
    CHUNK thisChunk = FirstFreeChunk;
    CHUNK* nextChunkPtr = &FirstFreeChunk;
    cell* dataArea  = NIL;
    
    // 遍历空闲空间链表
    
    for (thisChunk = FirstFreeChunk, nextChunkPtr = &FirstFreeChunk;
         thisChunk != NULL;
         nextChunkPtr = &thisChunk->next, thisChunk = thisChunk->next) {

        /* Calculate how much bigger or smaller the */
        /* chunk is than the requested size  进行size的比较*/
        long overhead = SIZE(thisChunk->size) + HEADERSIZE - size; // SIZE(thisChunk->size) + HEADERSIZE 当前FirstFreeChunk的大小
        // overhead = 当前FirstFreeChunk 与 size(请求分配空间大小) 的差额.
        // 如果剩余空间大与1的话,则直接使用Chunk,大于1的目的是还需要分配对象头
        if (overhead > HEADERSIZE) {
            thisChunk->size = (overhead - HEADERSIZE) << TYPEBITS;
            // 进行分配
            dataArea = (cell *)thisChunk + overhead;
            // 保存大小,不含对象头
            *dataArea = (size - HEADERSIZE) << TYPEBITS;
            return dataArea;
        } else if (overhead >= 0) {
            /* There was an exact match or overhead is too small to be useful.
             * Remove this chunk from the free list, and if there is extra
             * space at the end of the chunk, it becomes wasted space for the
             * lifetime of the allocated object
             * 将整个Chunk 进行分配,如果剩余空间是在 [0,1] 之间
             */
            *nextChunkPtr = thisChunk->next;
            dataArea = (cell *)thisChunk;
            /* Store the size of the object in the object header 保存大小 */
            // size + overhead - HEADERSIZE = size + SIZE(thisChunk->size) + HEADERSIZE - size -HEADERSIZE = SIZE(thisChunk->size)
            *dataArea = (size + overhead - HEADERSIZE) << TYPEBITS; // 此时保存的大小就是当前Chunk的大小.
            return dataArea;
        }
    }
    /* If we got here, there was no chunk with enough memory available 如果到了这里,则意味着没有适会的chunk*/
    return NULL;
}

至于垃圾收集和FirstFreeChunk的更多细节后续介绍.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值