admin管理员组文章数量:1576367
本文介绍初始化内存系统的代码.该方法定义如下:
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步:
- 初始化堆
- 初始化root
- 为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:
- 进行堆的初始化,如果初始化失败,则退出
- 初始化指针
第一步
第一步进行堆的初始化,如果初始化失败,则退出.调用的方法为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 --> 整个堆的终点地址.
结合目前,可以得出如下两图:
-
初始化时,分配的地址是4字节对齐的.
-
初始化时,分配的地址不是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的更多细节后续介绍.
版权声明:本文标题:kvm启动流程-002 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/dongtai/1727799930a1130701.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论