/* * Algorithm: * - Find bucket * - Check bucket pages - if it has enough free space, allocate that chunk * - Check pages in bigger buckets - if that has enough space, split that page and allocate from that chunk * - Allocate new page */ Allocation* Heap::Alloc(size_t bytes, ushort pdataCount, ushort xdataSize, bool canAllocInPreReservedHeapPageSegment, bool isAnyJittedCode, _Inout_ bool* isAllJITCodeInPreReservedRegion) { Assert(bytes > 0); Assert((allocXdata || pdataCount == 0) && (!allocXdata || pdataCount > 0)); Assert(pdataCount > 0 || (pdataCount == 0 && xdataSize == 0)); // Round up to power of two to allocate, and figure out which bucket to allocate in size_t bytesToAllocate = PowerOf2Policy::GetSize(bytes); BucketId bucket = (BucketId) GetBucketForSize(bytesToAllocate); Allocation* allocation; if (bucket == BucketId::LargeObjectList) { return AllocLargeObject(bytes, pdataCount, xdataSize, canAllocInPreReservedHeapPageSegment, isAnyJittedCode, isAllJITCodeInPreReservedRegion); } VerboseHeapTrace(L"Bucket is %d\n", bucket); VerboseHeapTrace(L"Requested: %d bytes. Allocated: %d bytes\n", bytes, bytesToAllocate); Page* page = nullptr; if(!this->buckets[bucket].Empty()) { page = &this->buckets[bucket].Head(); } else { page = FindPageToSplit(bucket, canAllocInPreReservedHeapPageSegment); } if(page == nullptr) { page = AllocNewPage(bucket, canAllocInPreReservedHeapPageSegment, isAnyJittedCode, isAllJITCodeInPreReservedRegion); } // Out of memory if (page == nullptr) { return nullptr; } allocation = AllocInPage(page, bytesToAllocate, pdataCount, xdataSize); return allocation; }
EmitBufferAllocation * EmitBufferManager<SyncObject>::NewAllocation(size_t bytes, ushort pdataCount, ushort xdataSize, bool canAllocInPreReservedHeapPageSegment, bool isAnyJittedCode) { FAULTINJECT_MEMORY_THROW(L"JIT", bytes); Assert(this->criticalSection.IsLocked()); bool isAllJITCodeInPreReservedRegion = true; CustomHeap::Allocation* heapAllocation = this->allocationHeap.Alloc(bytes, pdataCount, xdataSize, canAllocInPreReservedHeapPageSegment, isAnyJittedCode, &isAllJITCodeInPreReservedRegion); if (!isAllJITCodeInPreReservedRegion) { this->scriptContext->GetThreadContext()->ResetIsAllJITCodeInPreReservedRegion(); } if (heapAllocation == nullptr) { // This is used in interpreter scenario, thus we need to try to recover memory, if possible. // Can't simply throw as in JIT scenario, for which throw is what we want in order to give more mem to interpreter. JsUtil::ExternalApi::RecoverUnusedMemory(); heapAllocation = this->allocationHeap.Alloc(bytes, pdataCount, xdataSize, canAllocInPreReservedHeapPageSegment, isAnyJittedCode, &isAllJITCodeInPreReservedRegion); } if (heapAllocation == nullptr) { Js::Throw::OutOfMemory(); } AutoCustomHeapPointer allocatedMemory(&this->allocationHeap, heapAllocation); VerboseHeapTrace(L"New allocation: 0x%p, size: %p\n", heapAllocation->address, heapAllocation->size); EmitBufferAllocation * allocation = AnewStruct(this->allocator, EmitBufferAllocation); allocation->bytesCommitted = heapAllocation->size; allocation->allocation = allocatedMemory.Detach(); allocation->bytesUsed = 0; allocation->nextAllocation = this->allocations; allocation->recorded = false; this->allocations = allocation; #if DBG heapAllocation->isAllocationUsed = true; #endif #if DBG_DUMP this->totalBytesCommitted += heapAllocation->size; #endif return allocation; }
bool EmitBufferManager<SyncObject>::FreeAllocation(void* address) { AutoRealOrFakeCriticalSection<SyncObject> autoCs(&this->criticalSection); EmitBufferAllocation* previous = nullptr; EmitBufferAllocation* allocation = allocations; while(allocation != nullptr) { if (address >= allocation->allocation->address && address < (allocation->allocation->address + allocation->bytesUsed)) { if (previous == nullptr) { this->allocations = allocation->nextAllocation; } else { previous->nextAllocation = allocation->nextAllocation; } if ((scriptContext != nullptr) && allocation->recorded) { this->scriptContext->GetThreadContext()->SubCodeSize(allocation->bytesCommitted); } VerboseHeapTrace(_u("Freeing 0x%p, allocation: 0x%p\n"), address, allocation->allocation->address); this->allocationHeap.Free(allocation->allocation); this->allocator->Free(allocation, sizeof(EmitBufferAllocation)); return true; } previous = allocation; allocation = allocation->nextAllocation; } return false; }
bool EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::FreeAllocation(void* address) { #if PDATA_ENABLED && defined(_WIN32) DelayDeletingFunctionTable::Clear(); #endif AutoRealOrFakeCriticalSection<SyncObject> autoCs(&this->criticalSection); #if _M_ARM address = (void*)((uintptr_t)address & ~0x1); // clear the thumb bit #endif TEmitBufferAllocation* previous = nullptr; TEmitBufferAllocation* allocation = allocations; while(allocation != nullptr) { if (address == allocation->allocation->address) { if (previous == nullptr) { this->allocations = allocation->nextAllocation; } else { previous->nextAllocation = allocation->nextAllocation; } if ((scriptContext != nullptr) && allocation->recorded) { this->scriptContext->GetThreadContext()->SubCodeSize(allocation->bytesCommitted); } #if defined(_CONTROL_FLOW_GUARD) && !defined(_M_ARM) if (allocation->allocation->thunkAddress) { if (JITManager::GetJITManager()->IsJITServer()) { ((ServerThreadContext*)this->threadContext)->GetJITThunkEmitter()->FreeThunk(allocation->allocation->thunkAddress); } else { ((ThreadContext*)this->threadContext)->GetJITThunkEmitter()->FreeThunk(allocation->allocation->thunkAddress); } } else #endif { if (!JITManager::GetJITManager()->IsJITServer() || CONFIG_FLAG(OOPCFGRegistration)) { void* callTarget = address; #if _M_ARM callTarget = (void*)((uintptr_t)callTarget | 0x1); // add the thumb bit back, so we CFG-unregister the actual call target #endif threadContext->SetValidCallTargetForCFG(callTarget, false); } } VerboseHeapTrace(_u("Freeing 0x%p, allocation: 0x%p\n"), address, allocation->allocation->address); this->allocationHeap.Free(allocation->allocation); this->allocator->Free(allocation, sizeof(TEmitBufferAllocation)); return true; } previous = allocation; allocation = allocation->nextAllocation; } return false; }