/// <summary> /// Find the next free block in the smt /// </summary> /// <returns>Pointer to the start of block in the SMT. null if all SMT pages are full</returns> private static SMTBlock *NextFreeBlock() { var page = SMT; SMTBlock *pos = null; while (page != null && pos == null) { pos = NextFreeBlock(page); page = page->Next; } return(pos); }
private static SMTBlock *GetFirstWithSpace(uint aSize) { var page = SMT; SMTBlock *block = null; do { block = GetFirstWithSpace(page, aSize); page = page->Next; } while (block == null && page != null); return(block); }
/// <summary> /// Gets the last block on a certain page for objects of this size /// </summary> /// <param name="page">Page to search</param> /// <param name="aSize"></param> /// <returns></returns> private static SMTBlock *GetLastBlock(SMTPage *page, uint aSize) { SMTBlock *ptr = GetFirstBlock(page, aSize)->First; if (ptr == null) { return(null); } while (ptr->NextBlock != null) { ptr = ptr->NextBlock; } return(ptr); }
/// <summary> /// Get the first block for this size, which has space left to allocate to /// </summary> /// <param name="aSize"></param> /// <param name="root">The root node to start the search at</param> /// <returns></returns> private static SMTBlock *GetFirstWithSpace(uint aSize, RootSMTBlock *root) { SMTBlock *ptr = root->First; if (ptr == null) { return(null); } var lptr = ptr; while (ptr->SpacesLeft == 0 && ptr->NextBlock != null) { lptr = ptr; ptr = ptr->NextBlock; } if (ptr->SpacesLeft == 0 && ptr->NextBlock == null) { return(null); } return(ptr); }
/// <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> /// 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); }