/// <summary> /// Marks all objects referenced /// </summary> /// <param name="obj"></param> /// <param name="type"></param> public static void SweepTypedObject(uint *obj, uint type) { if (obj == null) { return; } uint fields = VTablesImpl.GetGCFieldCount(type); var offsets = VTablesImpl.GetGCFieldOffsets(type); var types = VTablesImpl.GetGCFieldTypes(type); for (int i = 0; i < fields; i++) { if (!VTablesImpl.IsValueType(types[i])) { var location = (uint *)((byte *)obj + offsets[i]) + 1; // +1 since we are only using 32bits from the 64bit if (*location != 0) // Check if its null { location = *(uint **)location; if (RAT.GetPageType(location) == RAT.PageType.HeapSmall) { MarkAndSweepObject(location); } } } else if (VTablesImpl.IsStruct(types[i])) { var obj1 = (uint *)((byte *)obj + offsets[i]); SweepTypedObject(obj1, types[i]); } } }
/// <summary> /// Create a page with the size of an item and add it to the SMT at a certain page /// </summary> /// <param name="aItemSize">Object size in bytes</param> /// <exception cref="Exception">Thrown if: /// <list type="bullet"> /// <item>aItemSize is 0.</item> /// <item>aItemSize is not word aligned.</item> /// <item>SMT is not initialized.</item> /// <item>The item size is bigger then a small heap size.</item> /// </list> /// </exception> static void CreatePage(SMTPage *aPage, uint aItemSize) { var xPtr = (byte *)RAT.AllocPages(RAT.PageType.HeapSmall, 1); if (xPtr == null) { return; // we failed to create the page, Alloc should still handle this case } uint xSlotSize = aItemSize + PrefixItemBytes; uint xItemCount = RAT.PageSize / xSlotSize; for (uint i = 0; i < xItemCount; i++) { byte * xSlotPtr = xPtr + i * xSlotSize; ushort *xMetaDataPtr = (ushort *)xSlotPtr; xMetaDataPtr[0] = 0; // Actual data size. 0 is empty. xMetaDataPtr[1] = 0; // Ref count } //now add it to the smt var parent = GetLastBlock(aPage, aItemSize); var smtBlock = NextFreeBlock(aPage); //get the next free block in the smt if (smtBlock == null) // we could not allocate a new block since the SMT table is all full { // we need to expand the SMT table by a page var last = SMT; while (last->Next != null) { last = last->Next; } last->Next = InitSMTPage(); parent = GetLastBlock(last->Next, aItemSize); smtBlock = NextFreeBlock(); if (smtBlock == null) { Debugger.SendKernelPanic(0x93); while (true) { } ; } } if (parent != null) { // there is already a block for the same size on the same page parent->NextBlock = smtBlock; } else { // in this case this is the first block of the size, so we can link it to root var root = GetFirstBlock(aPage, aItemSize); root->First = smtBlock; } smtBlock->SpacesLeft = xItemCount; smtBlock->PagePtr = xPtr; }
/// <summary> /// Free block. /// </summary> /// <param name="aPtr">A pointer to the block.</param> /// <exception cref="Exception">Thrown if page type is not found.</exception> static public void Free(void *aPtr) { // TODO - Should check the page type before freeing to make sure it is a Large? // or just trust the caller to avoid adding overhead? var xPageIdx = RAT.GetFirstRAT(aPtr); RAT.Free(xPageIdx); }
/// <summary> /// Alloc memory block, of a given size. /// </summary> /// <param name="aSize">A size of block to alloc, in bytes.</param> /// <returns>Byte pointer to the start of the block.</returns> static public byte *Alloc(Native aSize) { Native xPages = (Native)((aSize + PrefixBytes) / RAT.PageSize) + 1; var xPtr = (Native *)RAT.AllocPages(RAT.PageType.HeapLarge, xPages); xPtr[0] = xPages * RAT.PageSize - PrefixBytes; // Allocated data size xPtr[1] = aSize; // Actual data size xPtr[2] = 0; // Ref count xPtr[3] = 0; // Ptr to first return((byte *)xPtr + PrefixBytes); }
// Keep as void* and not byte* or other. Reduces typecasting from callers // who may have typed the pointer to their own needs. /// <summary> /// Free a heap item. /// </summary> /// <param name="aPtr">A pointer to the heap item to be freed.</param> /// <exception cref="Exception">Thrown if: /// <list type="bullet"> /// <item>Page type is not found.</item> /// <item>Heap item not found in RAT.</item> /// </list> /// </exception> public static void Free(void *aPtr) { //TODO find a better way to remove the double look up here for GetPageType and then again in the // .Free methods which actually free the entries in the RAT. var xType = RAT.GetPageType(aPtr); switch (xType) { case RAT.PageType.HeapLarge: HeapLarge.Free(aPtr); break; default: throw new Exception("Heap item not found in RAT."); } }
/// <summary> /// Create a page with the size of an item. /// </summary> /// <param name="aItemSize">Item size.</param> /// <exception cref="Exception">Thrown if: /// <list type="bullet"> /// <item>aItemSize is 0.</item> /// <item>aItemSize is not word aligned.</item> /// <item>SMT is not initialized.</item> /// <item>The item size is bigger then a small heap size.</item> /// </list> /// </exception> static void CreatePage(Native aItemSize) { if (aItemSize == 0) { throw new Exception("aItemSize cannot be 0."); } else if (aItemSize % sizeof(Native) != 0) { throw new Exception("aItemSize must be word aligned."); } else if (mMaxItemSize == 0) { throw new Exception("SMT is not initialized."); } else if (aItemSize > mMaxItemSize) { throw new Exception("Cannot allocate more than MaxItemSize in SmallHeap."); } // Limit to one page so they are allocated low and easy to move around. // In future may put some up top and be larger as well. Native xPages = 1; var xPtr = (Native *)RAT.AllocPages(RAT.PageType.HeapSmall, xPages); // Ptr to next page of same size xPtr[0] = 0; // // # of slots. Necessary for future use when more than one page, but used currently as well. Native xSlotSize = aItemSize + PrefixItemBytes; Native xItemCount = RAT.PageSize * xPages / xSlotSize; xPtr[1] = xItemCount; // // # of free slots xPtr[2] = xItemCount; // xPtr = xPtr + 3; for (Native i = 0; i < xItemCount; i++) { byte * xSlotPtr = (byte *)xPtr + i * xSlotSize; Native *xMetaDataPtr = (Native *)xSlotPtr; xMetaDataPtr[0] = 0; // Actual data size. 0 is empty. xMetaDataPtr[1] = 0; // Ref count xMetaDataPtr[2] = 0; // Ptr to first } }
/// <summary> /// Alloc memory block, of a given size. /// </summary> /// <param name="aSize">A size of block to alloc, in bytes.</param> /// <returns>Byte pointer to the start of the block.</returns> public static byte *Alloc(uint aSize, byte aType = RAT.PageType.HeapLarge) { uint xPages = ((aSize + PrefixBytes) / RAT.PageSize) + 1; var xPtr = (uint *)RAT.AllocPages(aType, xPages); if (xPtr == null) { Debugger.SendKernelPanic(0x67); // out of pages while (true) { } } xPtr[0] = xPages * RAT.PageSize - PrefixBytes; // Allocated data size xPtr[1] = aSize; // Actual data size xPtr[3] = 0; // padding for now?, xPtr[2] = 0; // padding + GC object status return((byte *)xPtr + PrefixBytes); }
/// <summary> /// Allocates and initialise a page for the SMT table /// </summary> /// <returns></returns> private static SMTPage *InitSMTPage() { var page = (SMTPage *)RAT.AllocPages(RAT.PageType.SMT, 1); page->First = (RootSMTBlock *)(page + 1); // TODO Change these sizes after further study and also when page size changes. // SMT can be grown as needed. Also can adjust and create new ones dynamicaly as it runs. // The current algorithm only works if we create the inital pages in increasing order AddRootSMTBlock(page, 16); AddRootSMTBlock(page, 24); AddRootSMTBlock(page, 48); AddRootSMTBlock(page, 64); AddRootSMTBlock(page, 128); AddRootSMTBlock(page, 256); AddRootSMTBlock(page, 512); AddRootSMTBlock(page, mMaxItemSize); return(page); }
// Keep as void* and not byte* or other. Reduces typecasting from callers // who may have typed the pointer to their own needs. /// <summary> /// Free a heap item. /// </summary> /// <param name="aPtr">A pointer to the heap item to be freed.</param> /// <exception cref="Exception">Thrown if: /// <list type="bullet"> /// <item>Page type is not found.</item> /// <item>Heap item not found in RAT.</item> /// </list> /// </exception> public static void Free(void *aPtr) { //TODO find a better way to remove the double look up here for GetPageType and then again in the // .Free methods which actually free the entries in the RAT. //Debugger.DoSendNumber(0x77); //Debugger.DoSendNumber((uint)aPtr); var xType = RAT.GetPageType(aPtr); switch (xType) { case RAT.PageType.HeapSmall: HeapSmall.Free(aPtr); break; case RAT.PageType.HeapMedium: case RAT.PageType.HeapLarge: HeapLarge.Free(aPtr); break; default: throw new Exception("Heap item not found in RAT."); } }
/// <summary> /// Init SMT (Size Map Table). /// </summary> /// <param name="aMaxItemSize">A max item size.</param> static void InitSMT(Native aMaxItemSize) { mMaxItemSize = aMaxItemSize; mSMT = (void **)RAT.AllocBytes(RAT.PageType.HeapSmall, mMaxItemSize * (Native)sizeof(void *)); }
/// <summary> /// Free a object /// </summary> /// <param name="aPtr">A pointer to the start object.</param> public static void Free(void *aPtr) { //Debugger.DoSendNumber(0x6666); //Debugger.DoSendNumber((uint)aPtr); var heapObject = (ushort *)aPtr; ushort size = heapObject[-2]; //Debugger.DoSendNumber(size); if (size == 0) { // double free, this object has already been freed Debugger.DoBochsBreak(); Debugger.DoSendNumber((uint)heapObject); Debugger.SendKernelPanic(0x99); } var allocated = (uint *)aPtr; allocated[-1] = 0; // zero both size and gc status at once // now zero the object so its ready for next allocation if (size < 4) // so we dont actually forget to clean up too small items { size = 4; } var bytes = size / 4; if (size % 4 != 0) { bytes += 1; } //Debugger.DoSendNumber(bytes); for (int i = 0; i < bytes; i++) { allocated[i] = 0; } // need to increase count in SMT again var allocatedOnPage = RAT.GetPagePtr(aPtr); var smtPage = SMT; SMTBlock *blockPtr = null; while (smtPage != null) { blockPtr = GetFirstBlock(smtPage, size)->First; while (blockPtr != null && blockPtr->PagePtr != allocatedOnPage) { blockPtr = blockPtr->NextBlock; } if (blockPtr->PagePtr == allocatedOnPage) { break; } smtPage = smtPage->Next; } if (blockPtr == null) { // this shouldnt happen Debugger.DoSendNumber((uint)aPtr); Debugger.DoSendNumber((uint)SMT); Debugger.SendKernelPanic(0x98); while (true) { } } blockPtr->SpacesLeft++; //Debugger.DoSend("Free finished"); }
/// <summary> /// Marks a GC managed object as referenced and recursivly marks child objects as well /// </summary> /// <param name="aPtr"></param> public static void MarkAndSweepObject(void *aPtr) { var gcPointer = (ObjectGCStatus *)aPtr; if ((gcPointer[-1] & ObjectGCStatus.Hit) == ObjectGCStatus.Hit) { return; // we already hit this object } // Mark gcPointer[-1] |= ObjectGCStatus.Hit; // Sweep uint *obj = (uint *)aPtr; // Check what we are dealing with if (*(obj + 1) == (uint)ObjectUtils.InstanceTypeEnum.NormalObject) { if (_StringType == 0) { _StringType = GetStringTypeID(); } var type = *obj; // Deal with strings first if (type == _StringType) { return; // we are done since they dont hold any reference to fields } SweepTypedObject(obj, type); } else if (*(obj + 1) == (uint)ObjectUtils.InstanceTypeEnum.Array) { var elementType = *obj; var length = *(obj + 2); var size = *(obj + 3); if (VTablesImpl.IsValueType(elementType)) { if (VTablesImpl.IsStruct(elementType)) { for (int i = 0; i < length; i++) { var location = (uint *)((byte *)obj + size * i) + 4; SweepTypedObject(location, elementType); } } } else { for (int i = 0; i < length; i++) { var location = (uint *)((byte *)obj + size * i) + 4 + 1; if (*location != 0) { location = *(uint **)location; if (RAT.GetPageType(location) == RAT.PageType.HeapSmall) // so we dont try free string literals { MarkAndSweepObject(location); } } } } } else if (*(obj + 1) == (uint)ObjectUtils.InstanceTypeEnum.BoxedValueType) { // do nothing } }
/// <summary> /// Collects all unreferenced objects after identifying them first /// </summary> /// <returns>Number of objects freed</returns> public static int Collect() { // Mark and sweep objects from roots // 1. Check if a page is in use if medium/large mark and sweep object // 2. Go throught the SMT table for small objects and go through pages by size // mark and sweep all allocated objects as well // Medium and large objects for (int ratIndex = 0; ratIndex < RAT.TotalPageCount; ratIndex++) { var pageType = *(RAT.mRAT + ratIndex); if (pageType == RAT.PageType.HeapMedium || pageType == RAT.PageType.HeapLarge) { var pagePtr = RAT.RamStart + ratIndex * RAT.PageSize; if (*(ushort *)(pagePtr + 3) != 0) { MarkAndSweepObject(pagePtr + HeapLarge.PrefixBytes); } } } // Small objects // we go one size at a time var rootSMTPtr = HeapSmall.SMT->First; while (rootSMTPtr != null) { uint size = rootSMTPtr->Size; var objectSize = size + HeapSmall.PrefixItemBytes; uint objectsPerPage = RAT.PageSize / objectSize; var smtBlock = rootSMTPtr->First; while (smtBlock != null) { var pagePtr = smtBlock->PagePtr; for (int i = 0; i < objectsPerPage; i++) { if (*(ushort *)(pagePtr + i * objectSize + 1) > 1) // 0 means not found and 1 means marked { MarkAndSweepObject(pagePtr + i * objectSize + HeapSmall.PrefixItemBytes); } } smtBlock = smtBlock->NextBlock; } rootSMTPtr = rootSMTPtr->LargerSize; } // Mark and sweep objects from stack uint *currentStackPointer = (uint *)CPU.GetEBPValue(); while (StackStart != currentStackPointer) { if (RAT.RamStart < (byte *)*currentStackPointer && (byte *)*currentStackPointer < RAT.HeapEnd) { if ((RAT.GetPageType((uint *)*currentStackPointer) & RAT.PageType.GCManaged) == RAT.PageType.GCManaged) { MarkAndSweepObject((uint *)*currentStackPointer); } } currentStackPointer += 1; } // Free all unreferenced and reset hit flag // This means we do the same transversal as we did before of the heap // but we done have to touch the stack again int freed = 0; // Medium and large objects for (int ratIndex = 0; ratIndex < RAT.TotalPageCount; ratIndex++) { var pageType = *(RAT.mRAT + ratIndex); if (pageType == RAT.PageType.HeapMedium || pageType == RAT.PageType.HeapLarge) { var pagePointer = RAT.RamStart + ratIndex * RAT.PageSize; if (*((ushort *)(pagePointer + HeapLarge.PrefixBytes) - 1) == 0) { Free(pagePointer + HeapLarge.PrefixBytes); freed += 1; } else { *((ushort *)(pagePointer + HeapLarge.PrefixBytes) - 1) &= (ushort)~ObjectGCStatus.Hit; } } } // Small objects // we go one size at a time rootSMTPtr = HeapSmall.SMT->First; while (rootSMTPtr != null) { uint size = rootSMTPtr->Size; uint objectSize = size + HeapSmall.PrefixItemBytes; uint objectsPerPage = RAT.PageSize / objectSize; SMTBlock *smtBlock = rootSMTPtr->First; while (smtBlock != null) { byte *pagePtr = smtBlock->PagePtr; for (int i = 0; i < objectsPerPage; i++) { if (*(ushort *)(pagePtr + i * objectSize) != 0) { if (*((ushort *)(pagePtr + i * objectSize) + 1) == 0) { Free(pagePtr + i * objectSize + HeapSmall.PrefixItemBytes); freed += 1; } else { *((ushort *)(pagePtr + i * objectSize) + 1) &= (ushort)~ObjectGCStatus.Hit; } } } smtBlock = smtBlock->NextBlock; } rootSMTPtr = rootSMTPtr->LargerSize; } return(freed); }
/// <summary> /// Free block. /// </summary> /// <param name="aPtr">A pointer to the block.</param> /// <exception cref="Exception">Thrown if page type is not found.</exception> public static void Free(void *aPtr) { var xPageIdx = RAT.GetFirstRATIndex(aPtr); RAT.Free(xPageIdx); }