runtime_loader: Resize heap areas instead of creating new ones.
This is more efficient and works most of the time. Additionally, we can potentially join with a previous free chunk in the allocator, avoiding extra fragmentation on the heap. app_server (on x86_64) only has 1 "rld heap" area after this change with a size of 0x50000 (320KB), whereas previously it had around 7 with a total size of 0x80000 (512KB).
This commit is contained in:
@@ -220,13 +220,10 @@ public:
|
|||||||
{
|
{
|
||||||
FreeChunk* chunk = (FreeChunk*)base;
|
FreeChunk* chunk = (FreeChunk*)base;
|
||||||
chunk->SetTo(size);
|
chunk->SetTo(size);
|
||||||
fFreeChunkTree.Insert(chunk);
|
|
||||||
|
|
||||||
fAvailable += chunk->Size();
|
|
||||||
#ifdef DEBUG_MAX_HEAP_USAGE
|
#ifdef DEBUG_MAX_HEAP_USAGE
|
||||||
fMaxHeapSize += chunk->Size();
|
fMaxHeapSize += chunk->Size();
|
||||||
fMaxHeapUsage = fMaxHeapSize - fAvailable;
|
|
||||||
#endif
|
#endif
|
||||||
|
_InsertChunk(chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 Available() const { return fAvailable; }
|
uint32 Available() const { return fAvailable; }
|
||||||
@@ -329,13 +326,35 @@ public:
|
|||||||
((uint32*)allocated)[i] = 0xdeadbeef;
|
((uint32*)allocated)[i] = 0xdeadbeef;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
_InsertChunk(freedChunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_MAX_HEAP_USAGE
|
||||||
|
uint32 MaxHeapSize() const { return fMaxHeapSize; }
|
||||||
|
uint32 MaxHeapUsage() const { return fMaxHeapUsage; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void DumpChunks()
|
||||||
|
{
|
||||||
|
FreeChunk* chunk = fFreeChunkTree.FindMin();
|
||||||
|
while (chunk != NULL) {
|
||||||
|
printf("\t%p: chunk size = %ld, end = %p, next = %p\n", chunk,
|
||||||
|
chunk->Size(), (uint8*)chunk + chunk->CompleteSize(),
|
||||||
|
chunk->Next());
|
||||||
|
chunk = chunk->Next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void _InsertChunk(FreeChunk* freedChunk)
|
||||||
|
{
|
||||||
// try to join the new free chunk with an existing one
|
// try to join the new free chunk with an existing one
|
||||||
// it may be joined with up to two chunks
|
// it may be joined with up to two chunks
|
||||||
|
|
||||||
FreeChunk* chunk = fFreeChunkTree.FindMin();
|
FreeChunk* chunk = fFreeChunkTree.FindMin();
|
||||||
int32 joinCount = 0;
|
int32 joinCount = 0;
|
||||||
|
|
||||||
while (chunk) {
|
while (chunk != NULL) {
|
||||||
FreeChunk* nextChunk = chunk->Next();
|
FreeChunk* nextChunk = chunk->Next();
|
||||||
|
|
||||||
if (chunk->IsTouching(freedChunk)) {
|
if (chunk->IsTouching(freedChunk)) {
|
||||||
@@ -358,22 +377,6 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_MAX_HEAP_USAGE
|
|
||||||
uint32 MaxHeapSize() const { return fMaxHeapSize; }
|
|
||||||
uint32 MaxHeapUsage() const { return fMaxHeapUsage; }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void DumpChunks()
|
|
||||||
{
|
|
||||||
FreeChunk* chunk = fFreeChunkTree.FindMin();
|
|
||||||
while (chunk != NULL) {
|
|
||||||
printf("\t%p: chunk size = %ld, end = %p, next = %p\n", chunk,
|
|
||||||
chunk->Size(), (uint8*)chunk + chunk->CompleteSize(),
|
|
||||||
chunk->Next());
|
|
||||||
chunk = chunk->Next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FreeChunkTree fFreeChunkTree;
|
FreeChunkTree fFreeChunkTree;
|
||||||
uint32 fAvailable;
|
uint32 fAvailable;
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ const static size_t kHeapGrowthAlignment = 32 * 1024;
|
|||||||
static const char* const kLockName = "runtime_loader heap";
|
static const char* const kLockName = "runtime_loader heap";
|
||||||
static recursive_lock sLock = RECURSIVE_LOCK_INITIALIZER(kLockName);
|
static recursive_lock sLock = RECURSIVE_LOCK_INITIALIZER(kLockName);
|
||||||
|
|
||||||
|
static void* sLastHeapBase;
|
||||||
|
static size_t sLastHeapAreaSize;
|
||||||
|
static area_id sLastHeapArea;
|
||||||
|
|
||||||
static SimpleAllocator<kAlignment> sAllocator;
|
static SimpleAllocator<kAlignment> sAllocator;
|
||||||
|
|
||||||
|
|
||||||
@@ -40,12 +44,26 @@ static SimpleAllocator<kAlignment> sAllocator;
|
|||||||
static status_t
|
static status_t
|
||||||
add_area(size_t size)
|
add_area(size_t size)
|
||||||
{
|
{
|
||||||
|
if (sLastHeapAreaSize != 0) {
|
||||||
|
// Try to resize the previous area instead of creating a new one.
|
||||||
|
status_t status = _kern_resize_area(sLastHeapArea, sLastHeapAreaSize + size);
|
||||||
|
if (status == B_OK) {
|
||||||
|
sAllocator.AddChunk((uint8*)sLastHeapBase + sLastHeapAreaSize, size);
|
||||||
|
sLastHeapAreaSize += size;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void* base;
|
void* base;
|
||||||
area_id area = _kern_create_area("rld heap", &base,
|
area_id area = _kern_create_area("rld heap", &base,
|
||||||
B_RANDOMIZED_ANY_ADDRESS, size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
|
B_RANDOMIZED_ANY_ADDRESS, size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
|
||||||
if (area < 0)
|
if (area < 0)
|
||||||
return area;
|
return area;
|
||||||
|
|
||||||
|
sLastHeapBase = base;
|
||||||
|
sLastHeapArea = area;
|
||||||
|
sLastHeapAreaSize = size;
|
||||||
|
|
||||||
sAllocator.AddChunk(base, size);
|
sAllocator.AddChunk(base, size);
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
@@ -54,7 +72,8 @@ add_area(size_t size)
|
|||||||
static status_t
|
static status_t
|
||||||
grow_heap(size_t bytes)
|
grow_heap(size_t bytes)
|
||||||
{
|
{
|
||||||
return add_area(sAllocator.Align(kAlignment + bytes, kHeapGrowthAlignment));
|
// Add kAlignment so that the heap has enough space for bookkeeping data.
|
||||||
|
return add_area(sAllocator.Align(bytes + kAlignment, kHeapGrowthAlignment));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -72,6 +91,7 @@ status_t
|
|||||||
heap_reinit_after_fork()
|
heap_reinit_after_fork()
|
||||||
{
|
{
|
||||||
recursive_lock_init(&sLock, kLockName);
|
recursive_lock_init(&sLock, kLockName);
|
||||||
|
sLastHeapArea = _kern_area_for(sLastHeapBase);
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user