[K/N] Track object counts during sweep ^KT-55364
Previously object count was tracked via allocator. It adds additional burden on every allocation. Tracking via sweeper is better because it mostly happens on the GC thread during a concurrent sweep.
This commit is contained in:
committed by
Space Cloud
parent
63231f7b73
commit
9fd05632f0
@@ -12,13 +12,11 @@ import kotlin.test.*
|
|||||||
@Test
|
@Test
|
||||||
fun `nothing new collected`() {
|
fun `nothing new collected`() {
|
||||||
GC.collect()
|
GC.collect()
|
||||||
GC.collect();
|
GC.collect()
|
||||||
val stat = GC.lastGCInfo
|
val stat = GC.lastGCInfo
|
||||||
assertNotNull(stat)
|
assertNotNull(stat)
|
||||||
assertEquals(stat.memoryUsageBefore.keys, stat.memoryUsageAfter.keys)
|
for (key in stat.sweepStatistics.keys) {
|
||||||
for (key in stat.memoryUsageBefore.keys) {
|
assertEquals(stat.sweepStatistics[key]!!.sweptCount, 0L)
|
||||||
assertEquals(stat.memoryUsageBefore[key]!!.objectsCount, stat.memoryUsageAfter[key]!!.objectsCount)
|
|
||||||
assertEquals(stat.memoryUsageBefore[key]!!.totalObjectsSizeBytes, stat.memoryUsageAfter[key]!!.totalObjectsSizeBytes)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ mm::ExtraObjectData* ExtraObjectPage::TryAllocate() noexcept {
|
|||||||
return freeBlock->Data();
|
return freeBlock->Data();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExtraObjectPage::Sweep(AtomicStack<ExtraObjectCell>& finalizerQueue) noexcept {
|
bool ExtraObjectPage::Sweep(gc::GCHandle::GCSweepExtraObjectsScope& sweepHandle, AtomicStack<ExtraObjectCell>& finalizerQueue) noexcept {
|
||||||
CustomAllocInfo("ExtraObjectPage(%p)::Sweep()", this);
|
CustomAllocInfo("ExtraObjectPage(%p)::Sweep()", this);
|
||||||
// `end` is after the last legal allocation of a block, but does not
|
// `end` is after the last legal allocation of a block, but does not
|
||||||
// necessarily match an actual block starting point.
|
// necessarily match an actual block starting point.
|
||||||
@@ -58,15 +58,24 @@ bool ExtraObjectPage::Sweep(AtomicStack<ExtraObjectCell>& finalizerQueue) noexce
|
|||||||
nextFree = &cell->next_;
|
nextFree = &cell->next_;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// If the current cell was marked, it's alive, and the whole page is alive.
|
auto status = SweepExtraObject(cell, finalizerQueue);
|
||||||
if (!SweepExtraObject(cell, finalizerQueue)) {
|
switch (status) {
|
||||||
alive = true;
|
case ExtraObjectStatus::TO_BE_FINALIZED:
|
||||||
continue;
|
sweepHandle.addMarkedObject();
|
||||||
|
// fallthrough
|
||||||
|
case ExtraObjectStatus::KEPT:
|
||||||
|
// If the current cell was marked, it's alive, and the whole page is alive.
|
||||||
|
alive = true;
|
||||||
|
sweepHandle.addKeptObject();
|
||||||
|
break;
|
||||||
|
case ExtraObjectStatus::SWEPT:
|
||||||
|
// Free the current block and insert it into the free list.
|
||||||
|
cell->next_ = *nextFree;
|
||||||
|
*nextFree = cell;
|
||||||
|
nextFree = &cell->next_;
|
||||||
|
sweepHandle.addSweptObject();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
// Free the current block and insert it into the free list.
|
|
||||||
cell->next_ = *nextFree;
|
|
||||||
*nextFree = cell;
|
|
||||||
nextFree = &cell->next_;
|
|
||||||
}
|
}
|
||||||
return alive;
|
return alive;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#include "AtomicStack.hpp"
|
#include "AtomicStack.hpp"
|
||||||
#include "ExtraObjectData.hpp"
|
#include "ExtraObjectData.hpp"
|
||||||
|
#include "GCStatistics.hpp"
|
||||||
|
|
||||||
namespace kotlin::alloc {
|
namespace kotlin::alloc {
|
||||||
|
|
||||||
@@ -34,7 +35,7 @@ public:
|
|||||||
// Tries to allocate in current page, returns null if no free block in page
|
// Tries to allocate in current page, returns null if no free block in page
|
||||||
mm::ExtraObjectData* TryAllocate() noexcept;
|
mm::ExtraObjectData* TryAllocate() noexcept;
|
||||||
|
|
||||||
bool Sweep(AtomicStack<ExtraObjectCell>& finalizerQueue) noexcept;
|
bool Sweep(gc::GCHandle::GCSweepExtraObjectsScope& sweepHandle, AtomicStack<ExtraObjectCell>& finalizerQueue) noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class AtomicStack<ExtraObjectPage>;
|
friend class AtomicStack<ExtraObjectPage>;
|
||||||
|
|||||||
@@ -41,7 +41,9 @@ TEST(CustomAllocTest, ExtraObjectPageConsequtiveAlloc) {
|
|||||||
TEST(CustomAllocTest, ExtraObjectPageSweepEmptyPage) {
|
TEST(CustomAllocTest, ExtraObjectPageSweepEmptyPage) {
|
||||||
Page* page = Page::Create();
|
Page* page = Page::Create();
|
||||||
Queue finalizerQueue;
|
Queue finalizerQueue;
|
||||||
EXPECT_FALSE(page->Sweep(finalizerQueue));
|
auto gcHandle = kotlin::gc::GCHandle::createFakeForTests();
|
||||||
|
auto gcScope = gcHandle.sweepExtraObjects();
|
||||||
|
EXPECT_FALSE(page->Sweep(gcScope, finalizerQueue));
|
||||||
EXPECT_EQ(finalizerQueue.size(), size_t(0));
|
EXPECT_EQ(finalizerQueue.size(), size_t(0));
|
||||||
page->Destroy();
|
page->Destroy();
|
||||||
}
|
}
|
||||||
@@ -56,7 +58,9 @@ TEST(CustomAllocTest, ExtraObjectPageSweepFullFinalizedPage) {
|
|||||||
}
|
}
|
||||||
EXPECT_EQ(count, EXTRA_OBJECT_COUNT);
|
EXPECT_EQ(count, EXTRA_OBJECT_COUNT);
|
||||||
Queue finalizerQueue;
|
Queue finalizerQueue;
|
||||||
EXPECT_FALSE(page->Sweep(finalizerQueue));
|
auto gcHandle = kotlin::gc::GCHandle::createFakeForTests();
|
||||||
|
auto gcScope = gcHandle.sweepExtraObjects();
|
||||||
|
EXPECT_FALSE(page->Sweep(gcScope, finalizerQueue));
|
||||||
EXPECT_EQ(finalizerQueue.size(), size_t(0));
|
EXPECT_EQ(finalizerQueue.size(), size_t(0));
|
||||||
page->Destroy();
|
page->Destroy();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ uint8_t* FixedBlockPage::TryAllocate() noexcept {
|
|||||||
return freeBlock->data;
|
return freeBlock->data;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FixedBlockPage::Sweep() noexcept {
|
bool FixedBlockPage::Sweep(gc::GCHandle::GCSweepScope& sweepHandle) noexcept {
|
||||||
CustomAllocInfo("FixedBlockPage(%p)::Sweep()", this);
|
CustomAllocInfo("FixedBlockPage(%p)::Sweep()", this);
|
||||||
// `end` is after the last legal allocation of a block, but does not
|
// `end` is after the last legal allocation of a block, but does not
|
||||||
// necessarily match an actual block starting point.
|
// necessarily match an actual block starting point.
|
||||||
@@ -62,6 +62,7 @@ bool FixedBlockPage::Sweep() noexcept {
|
|||||||
// If the current cell was marked, it's alive, and the whole page is alive.
|
// If the current cell was marked, it's alive, and the whole page is alive.
|
||||||
if (TryResetMark(cell)) {
|
if (TryResetMark(cell)) {
|
||||||
alive = true;
|
alive = true;
|
||||||
|
sweepHandle.addKeptObject();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
CustomAllocInfo("FixedBlockPage(%p)::Sweep: reclaim %p", this, cell);
|
CustomAllocInfo("FixedBlockPage(%p)::Sweep: reclaim %p", this, cell);
|
||||||
@@ -69,6 +70,7 @@ bool FixedBlockPage::Sweep() noexcept {
|
|||||||
cell->nextFree = *nextFree;
|
cell->nextFree = *nextFree;
|
||||||
*nextFree = cell;
|
*nextFree = cell;
|
||||||
nextFree = &cell->nextFree;
|
nextFree = &cell->nextFree;
|
||||||
|
sweepHandle.addSweptObject();
|
||||||
}
|
}
|
||||||
return alive;
|
return alive;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
#include "AtomicStack.hpp"
|
#include "AtomicStack.hpp"
|
||||||
|
#include "GCStatistics.hpp"
|
||||||
|
|
||||||
namespace kotlin::alloc {
|
namespace kotlin::alloc {
|
||||||
|
|
||||||
@@ -30,7 +31,7 @@ public:
|
|||||||
// Tries to allocate in current page, returns null if no free block in page
|
// Tries to allocate in current page, returns null if no free block in page
|
||||||
uint8_t* TryAllocate() noexcept;
|
uint8_t* TryAllocate() noexcept;
|
||||||
|
|
||||||
bool Sweep() noexcept;
|
bool Sweep(gc::GCHandle::GCSweepScope& sweepHandle) noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class AtomicStack<FixedBlockPage>;
|
friend class AtomicStack<FixedBlockPage>;
|
||||||
|
|||||||
@@ -44,52 +44,62 @@ TEST(CustomAllocTest, FixedBlockPageConsequtiveAlloc) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(CustomAllocTest, FixedBlockPageSweepEmptyPage) {
|
TEST(CustomAllocTest, FixedBlockPageSweepEmptyPage) {
|
||||||
|
auto gcHandle = kotlin::gc::GCHandle::createFakeForTests();
|
||||||
|
auto gcScope = gcHandle.sweep();
|
||||||
for (uint32_t size = 2; size <= FIXED_BLOCK_PAGE_MAX_BLOCK_SIZE; ++size) {
|
for (uint32_t size = 2; size <= FIXED_BLOCK_PAGE_MAX_BLOCK_SIZE; ++size) {
|
||||||
FixedBlockPage* page = FixedBlockPage::Create(size);
|
FixedBlockPage* page = FixedBlockPage::Create(size);
|
||||||
EXPECT_FALSE(page->Sweep());
|
EXPECT_FALSE(page->Sweep(gcScope));
|
||||||
page->Destroy();
|
page->Destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CustomAllocTest, FixedBlockPageSweepFullUnmarkedPage) {
|
TEST(CustomAllocTest, FixedBlockPageSweepFullUnmarkedPage) {
|
||||||
|
auto gcHandle = kotlin::gc::GCHandle::createFakeForTests();
|
||||||
|
auto gcScope = gcHandle.sweep();
|
||||||
for (uint32_t size = 2; size <= FIXED_BLOCK_PAGE_MAX_BLOCK_SIZE; ++size) {
|
for (uint32_t size = 2; size <= FIXED_BLOCK_PAGE_MAX_BLOCK_SIZE; ++size) {
|
||||||
FixedBlockPage* page = FixedBlockPage::Create(size);
|
FixedBlockPage* page = FixedBlockPage::Create(size);
|
||||||
uint32_t count = 0;
|
uint32_t count = 0;
|
||||||
while (alloc(page, size)) ++count;
|
while (alloc(page, size)) ++count;
|
||||||
EXPECT_EQ(count, FIXED_BLOCK_PAGE_CELL_COUNT / size);
|
EXPECT_EQ(count, FIXED_BLOCK_PAGE_CELL_COUNT / size);
|
||||||
EXPECT_FALSE(page->Sweep());
|
EXPECT_FALSE(page->Sweep(gcScope));
|
||||||
page->Destroy();
|
page->Destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CustomAllocTest, FixedBlockPageSweepSingleMarked) {
|
TEST(CustomAllocTest, FixedBlockPageSweepSingleMarked) {
|
||||||
|
auto gcHandle = kotlin::gc::GCHandle::createFakeForTests();
|
||||||
|
auto gcScope = gcHandle.sweep();
|
||||||
for (uint32_t size = 2; size <= FIXED_BLOCK_PAGE_MAX_BLOCK_SIZE; ++size) {
|
for (uint32_t size = 2; size <= FIXED_BLOCK_PAGE_MAX_BLOCK_SIZE; ++size) {
|
||||||
FixedBlockPage* page = FixedBlockPage::Create(size);
|
FixedBlockPage* page = FixedBlockPage::Create(size);
|
||||||
uint8_t* ptr = alloc(page, size);
|
uint8_t* ptr = alloc(page, size);
|
||||||
mark(ptr);
|
mark(ptr);
|
||||||
EXPECT_TRUE(page->Sweep());
|
EXPECT_TRUE(page->Sweep(gcScope));
|
||||||
page->Destroy();
|
page->Destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CustomAllocTest, FixedBlockPageSweepSingleReuse) {
|
TEST(CustomAllocTest, FixedBlockPageSweepSingleReuse) {
|
||||||
|
auto gcHandle = kotlin::gc::GCHandle::createFakeForTests();
|
||||||
|
auto gcScope = gcHandle.sweep();
|
||||||
for (uint32_t size = 2; size <= FIXED_BLOCK_PAGE_MAX_BLOCK_SIZE; ++size) {
|
for (uint32_t size = 2; size <= FIXED_BLOCK_PAGE_MAX_BLOCK_SIZE; ++size) {
|
||||||
FixedBlockPage* page = FixedBlockPage::Create(size);
|
FixedBlockPage* page = FixedBlockPage::Create(size);
|
||||||
uint8_t* ptr = alloc(page, size);
|
uint8_t* ptr = alloc(page, size);
|
||||||
EXPECT_FALSE(page->Sweep());
|
EXPECT_FALSE(page->Sweep(gcScope));
|
||||||
EXPECT_EQ(alloc(page, size), ptr);
|
EXPECT_EQ(alloc(page, size), ptr);
|
||||||
page->Destroy();
|
page->Destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CustomAllocTest, FixedBlockPageSweepReuse) {
|
TEST(CustomAllocTest, FixedBlockPageSweepReuse) {
|
||||||
|
auto gcHandle = kotlin::gc::GCHandle::createFakeForTests();
|
||||||
|
auto gcScope = gcHandle.sweep();
|
||||||
for (uint32_t size = 2; size <= FIXED_BLOCK_PAGE_MAX_BLOCK_SIZE; ++size) {
|
for (uint32_t size = 2; size <= FIXED_BLOCK_PAGE_MAX_BLOCK_SIZE; ++size) {
|
||||||
FixedBlockPage* page = FixedBlockPage::Create(size);
|
FixedBlockPage* page = FixedBlockPage::Create(size);
|
||||||
uint8_t* ptr;
|
uint8_t* ptr;
|
||||||
for (int count = 0; (ptr = alloc(page, size)); ++count) {
|
for (int count = 0; (ptr = alloc(page, size)); ++count) {
|
||||||
if (count % 2 == 0) mark(ptr);
|
if (count % 2 == 0) mark(ptr);
|
||||||
}
|
}
|
||||||
EXPECT_TRUE(page->Sweep());
|
EXPECT_TRUE(page->Sweep(gcScope));
|
||||||
uint32_t count = 0;
|
uint32_t count = 0;
|
||||||
for (; (ptr = alloc(page, size)); ++count) {
|
for (; (ptr = alloc(page, size)); ++count) {
|
||||||
if (count % 2 == 0) mark(ptr);
|
if (count % 2 == 0) mark(ptr);
|
||||||
|
|||||||
@@ -46,22 +46,22 @@ static bool IsAlive(ObjHeader* baseObject) noexcept {
|
|||||||
return objectData.marked();
|
return objectData.marked();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SweepExtraObject(ExtraObjectCell* extraObjectCell, AtomicStack<ExtraObjectCell>& finalizerQueue) noexcept {
|
ExtraObjectStatus SweepExtraObject(ExtraObjectCell* extraObjectCell, AtomicStack<ExtraObjectCell>& finalizerQueue) noexcept {
|
||||||
auto* extraObject = extraObjectCell->Data();
|
auto* extraObject = extraObjectCell->Data();
|
||||||
if (extraObject->getFlag(mm::ExtraObjectData::FLAGS_FINALIZED)) {
|
if (extraObject->getFlag(mm::ExtraObjectData::FLAGS_FINALIZED)) {
|
||||||
CustomAllocDebug("SweepIsCollectable(%p): already finalized", extraObject);
|
CustomAllocDebug("SweepIsCollectable(%p): already finalized", extraObject);
|
||||||
return true;
|
return ExtraObjectStatus::SWEPT;
|
||||||
}
|
}
|
||||||
auto* baseObject = extraObject->GetBaseObject();
|
auto* baseObject = extraObject->GetBaseObject();
|
||||||
RuntimeAssert(baseObject->heap(), "SweepIsCollectable on a non-heap object");
|
RuntimeAssert(baseObject->heap(), "SweepIsCollectable on a non-heap object");
|
||||||
if (extraObject->getFlag(mm::ExtraObjectData::FLAGS_IN_FINALIZER_QUEUE)) {
|
if (extraObject->getFlag(mm::ExtraObjectData::FLAGS_IN_FINALIZER_QUEUE)) {
|
||||||
CustomAllocDebug("SweepIsCollectable(%p): already in finalizer queue, keep base object (%p) alive", extraObject, baseObject);
|
CustomAllocDebug("SweepIsCollectable(%p): already in finalizer queue, keep base object (%p) alive", extraObject, baseObject);
|
||||||
KeepAlive(baseObject);
|
KeepAlive(baseObject);
|
||||||
return false;
|
return ExtraObjectStatus::TO_BE_FINALIZED;
|
||||||
}
|
}
|
||||||
if (IsAlive(baseObject)) {
|
if (IsAlive(baseObject)) {
|
||||||
CustomAllocDebug("SweepIsCollectable(%p): base object (%p) is alive", extraObject, baseObject);
|
CustomAllocDebug("SweepIsCollectable(%p): base object (%p) is alive", extraObject, baseObject);
|
||||||
return false;
|
return ExtraObjectStatus::KEPT;
|
||||||
}
|
}
|
||||||
extraObject->ClearRegularWeakReferenceImpl();
|
extraObject->ClearRegularWeakReferenceImpl();
|
||||||
if (extraObject->HasAssociatedObject()) {
|
if (extraObject->HasAssociatedObject()) {
|
||||||
@@ -69,18 +69,18 @@ bool SweepExtraObject(ExtraObjectCell* extraObjectCell, AtomicStack<ExtraObjectC
|
|||||||
finalizerQueue.Push(extraObjectCell);
|
finalizerQueue.Push(extraObjectCell);
|
||||||
KeepAlive(baseObject);
|
KeepAlive(baseObject);
|
||||||
CustomAllocDebug("SweepIsCollectable(%p): add to finalizerQueue", extraObject);
|
CustomAllocDebug("SweepIsCollectable(%p): add to finalizerQueue", extraObject);
|
||||||
return false;
|
return ExtraObjectStatus::TO_BE_FINALIZED;
|
||||||
} else {
|
} else {
|
||||||
if (HasFinalizers(baseObject)) {
|
if (HasFinalizers(baseObject)) {
|
||||||
extraObject->setFlag(mm::ExtraObjectData::FLAGS_IN_FINALIZER_QUEUE);
|
extraObject->setFlag(mm::ExtraObjectData::FLAGS_IN_FINALIZER_QUEUE);
|
||||||
finalizerQueue.Push(extraObjectCell);
|
finalizerQueue.Push(extraObjectCell);
|
||||||
KeepAlive(baseObject);
|
KeepAlive(baseObject);
|
||||||
CustomAllocDebug("SweepIsCollectable(%p): addings to finalizerQueue, keep base object (%p) alive", extraObject, baseObject);
|
CustomAllocDebug("SweepIsCollectable(%p): addings to finalizerQueue, keep base object (%p) alive", extraObject, baseObject);
|
||||||
return false;
|
return ExtraObjectStatus::TO_BE_FINALIZED;
|
||||||
}
|
}
|
||||||
extraObject->Uninstall();
|
extraObject->Uninstall();
|
||||||
CustomAllocDebug("SweepIsCollectable(%p): uninstalled extraObject", extraObject);
|
CustomAllocDebug("SweepIsCollectable(%p): uninstalled extraObject", extraObject);
|
||||||
return true;
|
return ExtraObjectStatus::SWEPT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,8 +18,13 @@ namespace kotlin::alloc {
|
|||||||
|
|
||||||
bool TryResetMark(void* ptr) noexcept;
|
bool TryResetMark(void* ptr) noexcept;
|
||||||
|
|
||||||
// Returns true if swept successfully, i.e., if the extraobject can be reclaimed now.
|
enum class ExtraObjectStatus {
|
||||||
bool SweepExtraObject(ExtraObjectCell* extraObjectCell, AtomicStack<ExtraObjectCell>& finalizerQueue) noexcept;
|
TO_BE_FINALIZED,
|
||||||
|
KEPT,
|
||||||
|
SWEPT,
|
||||||
|
};
|
||||||
|
|
||||||
|
ExtraObjectStatus SweepExtraObject(ExtraObjectCell* extraObjectCell, AtomicStack<ExtraObjectCell>& finalizerQueue) noexcept;
|
||||||
|
|
||||||
void* SafeAlloc(uint64_t size) noexcept;
|
void* SafeAlloc(uint64_t size) noexcept;
|
||||||
void Free(void* ptr, size_t size) noexcept;
|
void Free(void* ptr, size_t size) noexcept;
|
||||||
|
|||||||
@@ -40,21 +40,23 @@ void Heap::PrepareForGC() noexcept {
|
|||||||
usedExtraObjectPages_.TransferAllFrom(std::move(extraObjectPages_));
|
usedExtraObjectPages_.TransferAllFrom(std::move(extraObjectPages_));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Heap::Sweep() noexcept {
|
void Heap::Sweep(gc::GCHandle gcHandle) noexcept {
|
||||||
|
auto sweepHandle = gcHandle.sweep();
|
||||||
CustomAllocDebug("Heap::Sweep()");
|
CustomAllocDebug("Heap::Sweep()");
|
||||||
for (int blockSize = 0; blockSize <= FIXED_BLOCK_PAGE_MAX_BLOCK_SIZE; ++blockSize) {
|
for (int blockSize = 0; blockSize <= FIXED_BLOCK_PAGE_MAX_BLOCK_SIZE; ++blockSize) {
|
||||||
fixedBlockPages_[blockSize].Sweep();
|
fixedBlockPages_[blockSize].Sweep(sweepHandle);
|
||||||
}
|
}
|
||||||
nextFitPages_.Sweep();
|
nextFitPages_.Sweep(sweepHandle);
|
||||||
singleObjectPages_.SweepAndFree();
|
singleObjectPages_.SweepAndFree(sweepHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
AtomicStack<ExtraObjectCell> Heap::SweepExtraObjects(gc::GCHandle gcHandle) noexcept {
|
AtomicStack<ExtraObjectCell> Heap::SweepExtraObjects(gc::GCHandle gcHandle) noexcept {
|
||||||
|
auto sweepHandle = gcHandle.sweepExtraObjects();
|
||||||
CustomAllocDebug("Heap::SweepExtraObjects()");
|
CustomAllocDebug("Heap::SweepExtraObjects()");
|
||||||
AtomicStack<ExtraObjectCell> finalizerQueue;
|
AtomicStack<ExtraObjectCell> finalizerQueue;
|
||||||
ExtraObjectPage* page;
|
ExtraObjectPage* page;
|
||||||
while ((page = usedExtraObjectPages_.Pop())) {
|
while ((page = usedExtraObjectPages_.Pop())) {
|
||||||
if (!page->Sweep(finalizerQueue)) {
|
if (!page->Sweep(sweepHandle, finalizerQueue)) {
|
||||||
CustomAllocInfo("SweepExtraObjects free(%p)", page);
|
CustomAllocInfo("SweepExtraObjects free(%p)", page);
|
||||||
page->Destroy();
|
page->Destroy();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ public:
|
|||||||
// Sweep through all remaining pages, freeing those blocks where CanReclaim
|
// Sweep through all remaining pages, freeing those blocks where CanReclaim
|
||||||
// returns true. If multiple sweepers are active, each page will only be
|
// returns true. If multiple sweepers are active, each page will only be
|
||||||
// seen by one sweeper.
|
// seen by one sweeper.
|
||||||
void Sweep() noexcept;
|
void Sweep(gc::GCHandle gcHandle) noexcept;
|
||||||
|
|
||||||
AtomicStack<ExtraObjectCell> SweepExtraObjects(gc::GCHandle gcHandle) noexcept;
|
AtomicStack<ExtraObjectCell> SweepExtraObjects(gc::GCHandle gcHandle) noexcept;
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ TEST(CustomAllocTest, HeapReuseFixedBlockPages) {
|
|||||||
mark(obj); // to make the page survive a sweep
|
mark(obj); // to make the page survive a sweep
|
||||||
}
|
}
|
||||||
heap.PrepareForGC();
|
heap.PrepareForGC();
|
||||||
heap.Sweep();
|
auto gcHandle = kotlin::gc::GCHandle::createFakeForTests();
|
||||||
|
heap.Sweep(gcHandle);
|
||||||
for (int blocks = MIN; blocks < MAX; ++blocks) {
|
for (int blocks = MIN; blocks < MAX; ++blocks) {
|
||||||
EXPECT_EQ(pages[blocks], heap.GetFixedBlockPage(blocks));
|
EXPECT_EQ(pages[blocks], heap.GetFixedBlockPage(blocks));
|
||||||
}
|
}
|
||||||
@@ -49,7 +50,8 @@ TEST(CustomAllocTest, HeapReuseNextFitPages) {
|
|||||||
void* obj = page->TryAllocate(BLOCKSIZE);
|
void* obj = page->TryAllocate(BLOCKSIZE);
|
||||||
mark(obj); // to make the page survive a sweep
|
mark(obj); // to make the page survive a sweep
|
||||||
heap.PrepareForGC();
|
heap.PrepareForGC();
|
||||||
heap.Sweep();
|
auto gcHandle = kotlin::gc::GCHandle::createFakeForTests();
|
||||||
|
heap.Sweep(gcHandle);
|
||||||
EXPECT_EQ(page, heap.GetNextFitPage(BLOCKSIZE));
|
EXPECT_EQ(page, heap.GetNextFitPage(BLOCKSIZE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ uint8_t* NextFitPage::TryAllocate(uint32_t blockSize) noexcept {
|
|||||||
return curBlock_->TryAllocate(cellsNeeded);
|
return curBlock_->TryAllocate(cellsNeeded);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NextFitPage::Sweep() noexcept {
|
bool NextFitPage::Sweep(gc::GCHandle::GCSweepScope& sweepHandle) noexcept {
|
||||||
CustomAllocDebug("NextFitPage@%p::Sweep()", this);
|
CustomAllocDebug("NextFitPage@%p::Sweep()", this);
|
||||||
Cell* end = cells_ + NEXT_FIT_PAGE_CELL_COUNT;
|
Cell* end = cells_ + NEXT_FIT_PAGE_CELL_COUNT;
|
||||||
bool alive = false;
|
bool alive = false;
|
||||||
@@ -47,8 +47,10 @@ bool NextFitPage::Sweep() noexcept {
|
|||||||
if (block->isAllocated_) {
|
if (block->isAllocated_) {
|
||||||
if (TryResetMark(block->data_)) {
|
if (TryResetMark(block->data_)) {
|
||||||
alive = true;
|
alive = true;
|
||||||
|
sweepHandle.addKeptObject();
|
||||||
} else {
|
} else {
|
||||||
block->Deallocate();
|
block->Deallocate();
|
||||||
|
sweepHandle.addSweptObject();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#include "AtomicStack.hpp"
|
#include "AtomicStack.hpp"
|
||||||
#include "Cell.hpp"
|
#include "Cell.hpp"
|
||||||
|
#include "GCStatistics.hpp"
|
||||||
|
|
||||||
namespace kotlin::alloc {
|
namespace kotlin::alloc {
|
||||||
|
|
||||||
@@ -23,7 +24,7 @@ public:
|
|||||||
// Tries to allocate in current page, returns null if no free block in page is big enough
|
// Tries to allocate in current page, returns null if no free block in page is big enough
|
||||||
uint8_t* TryAllocate(uint32_t blockSize) noexcept;
|
uint8_t* TryAllocate(uint32_t blockSize) noexcept;
|
||||||
|
|
||||||
bool Sweep() noexcept;
|
bool Sweep(gc::GCHandle::GCSweepScope& sweepHandle) noexcept;
|
||||||
|
|
||||||
// Testing method
|
// Testing method
|
||||||
bool CheckInvariants() noexcept;
|
bool CheckInvariants() noexcept;
|
||||||
|
|||||||
@@ -52,7 +52,9 @@ TEST(CustomAllocTest, NextFitPageAlloc) {
|
|||||||
|
|
||||||
TEST(CustomAllocTest, NextFitPageSweepEmptyPage) {
|
TEST(CustomAllocTest, NextFitPageSweepEmptyPage) {
|
||||||
NextFitPage* page = NextFitPage::Create(MIN_BLOCK_SIZE);
|
NextFitPage* page = NextFitPage::Create(MIN_BLOCK_SIZE);
|
||||||
EXPECT_FALSE(page->Sweep());
|
auto gcHandle = kotlin::gc::GCHandle::createFakeForTests();
|
||||||
|
auto gcScope = gcHandle.sweep();
|
||||||
|
EXPECT_FALSE(page->Sweep(gcScope));
|
||||||
page->Destroy();
|
page->Destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +63,9 @@ TEST(CustomAllocTest, NextFitPageSweepFullUnmarkedPage) {
|
|||||||
std::minstd_rand r(seed);
|
std::minstd_rand r(seed);
|
||||||
NextFitPage* page = NextFitPage::Create(MIN_BLOCK_SIZE);
|
NextFitPage* page = NextFitPage::Create(MIN_BLOCK_SIZE);
|
||||||
while (alloc(page, MIN_BLOCK_SIZE + r() % 100)) {}
|
while (alloc(page, MIN_BLOCK_SIZE + r() % 100)) {}
|
||||||
EXPECT_FALSE(page->Sweep());
|
auto gcHandle = kotlin::gc::GCHandle::createFakeForTests();
|
||||||
|
auto gcScope = gcHandle.sweep();
|
||||||
|
EXPECT_FALSE(page->Sweep(gcScope));
|
||||||
page->Destroy();
|
page->Destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -69,17 +73,21 @@ TEST(CustomAllocTest, NextFitPageSweepFullUnmarkedPage) {
|
|||||||
TEST(CustomAllocTest, NextFitPageSweepSingleMarked) {
|
TEST(CustomAllocTest, NextFitPageSweepSingleMarked) {
|
||||||
NextFitPage* page = NextFitPage::Create(MIN_BLOCK_SIZE);
|
NextFitPage* page = NextFitPage::Create(MIN_BLOCK_SIZE);
|
||||||
mark(alloc(page, MIN_BLOCK_SIZE));
|
mark(alloc(page, MIN_BLOCK_SIZE));
|
||||||
EXPECT_TRUE(page->Sweep());
|
auto gcHandle = kotlin::gc::GCHandle::createFakeForTests();
|
||||||
|
auto gcScope = gcHandle.sweep();
|
||||||
|
EXPECT_TRUE(page->Sweep(gcScope));
|
||||||
page->Destroy();
|
page->Destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CustomAllocTest, NextFitPageSweepSingleReuse) {
|
TEST(CustomAllocTest, NextFitPageSweepSingleReuse) {
|
||||||
|
auto gcHandle = kotlin::gc::GCHandle::createFakeForTests();
|
||||||
|
auto gcScope = gcHandle.sweep();
|
||||||
for (uint32_t seed = 0xC0FFEE0; seed <= 0xC0FFEEF; ++seed) {
|
for (uint32_t seed = 0xC0FFEE0; seed <= 0xC0FFEEF; ++seed) {
|
||||||
std::minstd_rand r(seed);
|
std::minstd_rand r(seed);
|
||||||
NextFitPage* page = NextFitPage::Create(MIN_BLOCK_SIZE);
|
NextFitPage* page = NextFitPage::Create(MIN_BLOCK_SIZE);
|
||||||
int count1 = 0;
|
int count1 = 0;
|
||||||
while (alloc(page, MIN_BLOCK_SIZE + r() % 100)) ++count1;
|
while (alloc(page, MIN_BLOCK_SIZE + r() % 100)) ++count1;
|
||||||
EXPECT_FALSE(page->Sweep());
|
EXPECT_FALSE(page->Sweep(gcScope));
|
||||||
r.seed(seed);
|
r.seed(seed);
|
||||||
int count2 = 0;
|
int count2 = 0;
|
||||||
while (alloc(page, MIN_BLOCK_SIZE + r() % 100)) ++count2;
|
while (alloc(page, MIN_BLOCK_SIZE + r() % 100)) ++count2;
|
||||||
@@ -89,6 +97,8 @@ TEST(CustomAllocTest, NextFitPageSweepSingleReuse) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(CustomAllocTest, NextFitPageSweepReuse) {
|
TEST(CustomAllocTest, NextFitPageSweepReuse) {
|
||||||
|
auto gcHandle = kotlin::gc::GCHandle::createFakeForTests();
|
||||||
|
auto gcScope = gcHandle.sweep();
|
||||||
for (uint32_t seed = 0xC0FFEE0; seed <= 0xC0FFEEF; ++seed) {
|
for (uint32_t seed = 0xC0FFEE0; seed <= 0xC0FFEEF; ++seed) {
|
||||||
std::minstd_rand r(seed);
|
std::minstd_rand r(seed);
|
||||||
NextFitPage* page = NextFitPage::Create(MIN_BLOCK_SIZE);
|
NextFitPage* page = NextFitPage::Create(MIN_BLOCK_SIZE);
|
||||||
@@ -102,7 +112,7 @@ TEST(CustomAllocTest, NextFitPageSweepReuse) {
|
|||||||
++unmarked;
|
++unmarked;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
page->Sweep();
|
page->Sweep(gcScope);
|
||||||
int freed = 0;
|
int freed = 0;
|
||||||
while (alloc(page, MIN_BLOCK_SIZE)) ++freed;
|
while (alloc(page, MIN_BLOCK_SIZE)) ++freed;
|
||||||
EXPECT_EQ(freed, unmarked);
|
EXPECT_EQ(freed, unmarked);
|
||||||
@@ -111,9 +121,11 @@ TEST(CustomAllocTest, NextFitPageSweepReuse) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(CustomAllocTest, NextFitPageSweepCoallesce) {
|
TEST(CustomAllocTest, NextFitPageSweepCoallesce) {
|
||||||
|
auto gcHandle = kotlin::gc::GCHandle::createFakeForTests();
|
||||||
|
auto gcScope = gcHandle.sweep();
|
||||||
NextFitPage* page = NextFitPage::Create(MIN_BLOCK_SIZE);
|
NextFitPage* page = NextFitPage::Create(MIN_BLOCK_SIZE);
|
||||||
EXPECT_TRUE(alloc(page, (NEXT_FIT_PAGE_CELL_COUNT-1) / 2 - 1));
|
EXPECT_TRUE(alloc(page, (NEXT_FIT_PAGE_CELL_COUNT-1) / 2 - 1));
|
||||||
EXPECT_FALSE(page->Sweep());
|
EXPECT_FALSE(page->Sweep(gcScope));
|
||||||
EXPECT_TRUE(alloc(page, (NEXT_FIT_PAGE_CELL_COUNT-1) - 1));
|
EXPECT_TRUE(alloc(page, (NEXT_FIT_PAGE_CELL_COUNT-1) - 1));
|
||||||
page->Destroy();
|
page->Destroy();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
#include "AtomicStack.hpp"
|
#include "AtomicStack.hpp"
|
||||||
|
#include "GCStatistics.hpp"
|
||||||
|
|
||||||
namespace kotlin::alloc {
|
namespace kotlin::alloc {
|
||||||
|
|
||||||
@@ -22,14 +23,15 @@ public:
|
|||||||
while ((page = empty_.Pop())) page->Destroy();
|
while ((page = empty_.Pop())) page->Destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sweep() noexcept {
|
void Sweep(gc::GCHandle::GCSweepScope& sweepHandle) noexcept {
|
||||||
while (SweepSingle(unswept_, ready_)) {}
|
while (SweepSingle(sweepHandle, unswept_.Pop(), unswept_, ready_)) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SweepAndFree() noexcept {
|
void SweepAndFree(gc::GCHandle::GCSweepScope& sweepHandle) noexcept {
|
||||||
T* page;
|
T* page;
|
||||||
while ((page = unswept_.Pop())) {
|
while ((page = unswept_.Pop())) {
|
||||||
if (page->Sweep()) {
|
if (page->Sweep(sweepHandle)) {
|
||||||
ready_.Push(page);
|
ready_.Push(page);
|
||||||
} else {
|
} else {
|
||||||
page->Destroy();
|
page->Destroy();
|
||||||
@@ -39,8 +41,12 @@ public:
|
|||||||
|
|
||||||
T* GetPage(uint32_t cellCount) noexcept {
|
T* GetPage(uint32_t cellCount) noexcept {
|
||||||
T* page;
|
T* page;
|
||||||
if ((page = SweepSingle(unswept_, used_))) {
|
if ((page = unswept_.Pop())) {
|
||||||
return page;
|
// If there're unswept_ pages, the GC is in progress.
|
||||||
|
auto sweepHandle = gc::GCHandle::currentEpoch()->sweep();
|
||||||
|
if ((page = SweepSingle(sweepHandle, page, unswept_, used_))) {
|
||||||
|
return page;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ((page = ready_.Pop())) {
|
if ((page = ready_.Pop())) {
|
||||||
used_.Push(page);
|
used_.Push(page);
|
||||||
@@ -68,15 +74,17 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T* SweepSingle(AtomicStack<T>& from, AtomicStack<T>& to) noexcept {
|
T* SweepSingle(gc::GCHandle::GCSweepScope& sweepHandle, T* page, AtomicStack<T>& from, AtomicStack<T>& to) noexcept {
|
||||||
T* page;
|
if (!page) {
|
||||||
while ((page = from.Pop())) {
|
return nullptr;
|
||||||
if (page->Sweep()) {
|
}
|
||||||
|
do {
|
||||||
|
if (page->Sweep(sweepHandle)) {
|
||||||
to.Push(page);
|
to.Push(page);
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
empty_.Push(page);
|
empty_.Push(page);
|
||||||
}
|
} while ((page = from.Pop()));
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,12 +37,14 @@ uint8_t* SingleObjectPage::TryAllocate() noexcept {
|
|||||||
return Data();
|
return Data();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SingleObjectPage::Sweep() noexcept {
|
bool SingleObjectPage::Sweep(gc::GCHandle::GCSweepScope& sweepHandle) noexcept {
|
||||||
CustomAllocDebug("SingleObjectPage@%p::Sweep()", this);
|
CustomAllocDebug("SingleObjectPage@%p::Sweep()", this);
|
||||||
if (!TryResetMark(Data())) {
|
if (!TryResetMark(Data())) {
|
||||||
isAllocated_ = false;
|
isAllocated_ = false;
|
||||||
|
sweepHandle.addKeptObject();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
sweepHandle.addSweptObject();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
#include "AtomicStack.hpp"
|
#include "AtomicStack.hpp"
|
||||||
|
#include "GCStatistics.hpp"
|
||||||
|
|
||||||
namespace kotlin::alloc {
|
namespace kotlin::alloc {
|
||||||
|
|
||||||
@@ -23,7 +24,7 @@ public:
|
|||||||
|
|
||||||
uint8_t* TryAllocate() noexcept;
|
uint8_t* TryAllocate() noexcept;
|
||||||
|
|
||||||
bool Sweep() noexcept;
|
bool Sweep(gc::GCHandle::GCSweepScope& sweepHandle) noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class AtomicStack<SingleObjectPage>;
|
friend class AtomicStack<SingleObjectPage>;
|
||||||
|
|||||||
@@ -34,7 +34,9 @@ SingleObjectPage* alloc(uint64_t blockSize) {
|
|||||||
TEST(CustomAllocTest, SingleObjectPageSweepEmptyPage) {
|
TEST(CustomAllocTest, SingleObjectPageSweepEmptyPage) {
|
||||||
SingleObjectPage* page = alloc(MIN_BLOCK_SIZE);
|
SingleObjectPage* page = alloc(MIN_BLOCK_SIZE);
|
||||||
EXPECT_TRUE(page);
|
EXPECT_TRUE(page);
|
||||||
EXPECT_FALSE(page->Sweep());
|
auto gcHandle = kotlin::gc::GCHandle::createFakeForTests();
|
||||||
|
auto gcScope = gcHandle.sweep();
|
||||||
|
EXPECT_FALSE(page->Sweep(gcScope));
|
||||||
page->Destroy();
|
page->Destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +45,9 @@ TEST(CustomAllocTest, SingleObjectPageSweepFullPage) {
|
|||||||
EXPECT_TRUE(page);
|
EXPECT_TRUE(page);
|
||||||
EXPECT_TRUE(page->Data());
|
EXPECT_TRUE(page->Data());
|
||||||
mark(page->Data());
|
mark(page->Data());
|
||||||
EXPECT_TRUE(page->Sweep());
|
auto gcHandle = kotlin::gc::GCHandle::createFakeForTests();
|
||||||
|
auto gcScope = gcHandle.sweep();
|
||||||
|
EXPECT_TRUE(page->Sweep(gcScope));
|
||||||
page->Destroy();
|
page->Destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ bool gc::ConcurrentMarkAndSweep::PerformFullGC(int64_t epoch) noexcept {
|
|||||||
|
|
||||||
mm::WaitForThreadsSuspension();
|
mm::WaitForThreadsSuspension();
|
||||||
auto markStats = gcHandle.getMarked();
|
auto markStats = gcHandle.getMarked();
|
||||||
scheduler.gcData().UpdateAliveSetBytes(markStats.totalObjectsSize);
|
scheduler.gcData().UpdateAliveSetBytes(markStats.markedSizeBytes);
|
||||||
|
|
||||||
gc::processWeaks<ProcessWeaksTraits>(gcHandle, mm::SpecialRefRegistry::instance());
|
gc::processWeaks<ProcessWeaksTraits>(gcHandle, mm::SpecialRefRegistry::instance());
|
||||||
|
|
||||||
@@ -207,7 +207,7 @@ bool gc::ConcurrentMarkAndSweep::PerformFullGC(int64_t epoch) noexcept {
|
|||||||
|
|
||||||
mm::ResumeThreads();
|
mm::ResumeThreads();
|
||||||
gcHandle.threadsAreResumed();
|
gcHandle.threadsAreResumed();
|
||||||
heap_.Sweep();
|
heap_.Sweep(gcHandle);
|
||||||
#endif
|
#endif
|
||||||
state_.finish(epoch);
|
state_.finish(epoch);
|
||||||
gcHandle.finalizersScheduled(finalizerQueue.size());
|
gcHandle.finalizersScheduled(finalizerQueue.size());
|
||||||
|
|||||||
@@ -95,36 +95,6 @@ size_t gc::GC::GetAllocatedHeapSize(ObjHeader* object) noexcept {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
size_t gc::GC::GetHeapObjectsCountUnsafe() const noexcept {
|
|
||||||
#ifndef CUSTOM_ALLOCATOR
|
|
||||||
return impl_->objectFactory().GetObjectsCountUnsafe();
|
|
||||||
#else
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
size_t gc::GC::GetTotalHeapObjectsSizeUnsafe() const noexcept {
|
|
||||||
#ifndef CUSTOM_ALLOCATOR
|
|
||||||
return impl_->objectFactory().GetTotalObjectsSizeUnsafe();
|
|
||||||
#else
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
size_t gc::GC::GetExtraObjectsCountUnsafe() const noexcept {
|
|
||||||
#ifndef CUSTOM_ALLOCATOR
|
|
||||||
return mm::GlobalData::Instance().extraObjectDataFactory().GetSizeUnsafe();
|
|
||||||
#else
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
size_t gc::GC::GetTotalExtraObjectsSizeUnsafe() const noexcept {
|
|
||||||
#ifndef CUSTOM_ALLOCATOR
|
|
||||||
return mm::GlobalData::Instance().extraObjectDataFactory().GetTotalObjectsSizeUnsafe();
|
|
||||||
#else
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t gc::GC::GetTotalHeapObjectsSizeBytes() const noexcept {
|
size_t gc::GC::GetTotalHeapObjectsSizeBytes() const noexcept {
|
||||||
return allocatedBytes();
|
return allocatedBytes();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,10 +59,6 @@ public:
|
|||||||
|
|
||||||
static size_t GetAllocatedHeapSize(ObjHeader* object) noexcept;
|
static size_t GetAllocatedHeapSize(ObjHeader* object) noexcept;
|
||||||
|
|
||||||
size_t GetHeapObjectsCountUnsafe() const noexcept;
|
|
||||||
size_t GetTotalHeapObjectsSizeUnsafe() const noexcept;
|
|
||||||
size_t GetExtraObjectsCountUnsafe() const noexcept;
|
|
||||||
size_t GetTotalExtraObjectsSizeUnsafe() const noexcept;
|
|
||||||
size_t GetTotalHeapObjectsSizeBytes() const noexcept;
|
size_t GetTotalHeapObjectsSizeBytes() const noexcept;
|
||||||
|
|
||||||
gc::GCSchedulerConfig& gcSchedulerConfig() noexcept;
|
gc::GCSchedulerConfig& gcSchedulerConfig() noexcept;
|
||||||
|
|||||||
@@ -26,26 +26,41 @@ void Kotlin_Internal_GC_GCInfoBuilder_setPostGcCleanupTime(KRef thiz, KLong valu
|
|||||||
void Kotlin_Internal_GC_GCInfoBuilder_setRootSet(KRef thiz,
|
void Kotlin_Internal_GC_GCInfoBuilder_setRootSet(KRef thiz,
|
||||||
KLong threadLocalReferences, KLong stackReferences,
|
KLong threadLocalReferences, KLong stackReferences,
|
||||||
KLong globalReferences, KLong stableReferences);
|
KLong globalReferences, KLong stableReferences);
|
||||||
void Kotlin_Internal_GC_GCInfoBuilder_setMemoryUsageBefore(KRef thiz, KNativePtr name, KLong objectsCount, KLong totalObjectsSize);
|
void Kotlin_Internal_GC_GCInfoBuilder_setMarkStats(KRef thiz, KLong markedCount);
|
||||||
void Kotlin_Internal_GC_GCInfoBuilder_setMemoryUsageAfter(KRef thiz, KNativePtr name, KLong objectsCount, KLong totalObjectsSize);
|
void Kotlin_Internal_GC_GCInfoBuilder_setSweepStats(KRef thiz, KNativePtr name, KLong sweptCount, KLong keptCount);
|
||||||
|
void Kotlin_Internal_GC_GCInfoBuilder_setMemoryUsageBefore(KRef thiz, KNativePtr name, KLong sizeBytes);
|
||||||
|
void Kotlin_Internal_GC_GCInfoBuilder_setMemoryUsageAfter(KRef thiz, KNativePtr name, KLong sizeBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
constexpr KNativePtr heapPoolKey = const_cast<KNativePtr>(static_cast<const void*>("heap"));
|
||||||
|
constexpr KNativePtr extraPoolKey = const_cast<KNativePtr>(static_cast<const void*>("extra"));
|
||||||
|
|
||||||
|
struct MemoryUsage {
|
||||||
|
uint64_t sizeBytes;
|
||||||
|
};
|
||||||
|
|
||||||
struct MemoryUsageMap {
|
struct MemoryUsageMap {
|
||||||
std::optional<kotlin::gc::MemoryUsage> heap;
|
std::optional<MemoryUsage> heap;
|
||||||
std::optional<kotlin::gc::MemoryUsage> extra;
|
|
||||||
|
void build(KRef builder, void (*add)(KRef, KNativePtr, KLong)) {
|
||||||
|
if (heap) {
|
||||||
|
add(builder, heapPoolKey, static_cast<KLong>(heap->sizeBytes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SweepStatsMap {
|
||||||
|
std::optional<gc::SweepStats> heap;
|
||||||
|
std::optional<gc::SweepStats> extra;
|
||||||
|
|
||||||
void build(KRef builder, void (*add)(KRef, KNativePtr, KLong, KLong)) {
|
void build(KRef builder, void (*add)(KRef, KNativePtr, KLong, KLong)) {
|
||||||
if (heap) {
|
if (heap) {
|
||||||
add(builder, const_cast<KNativePtr>(static_cast<const void*>("heap")),
|
add(builder, heapPoolKey, static_cast<KLong>(heap->keptCount), static_cast<KLong>(heap->sweptCount));
|
||||||
static_cast<KLong>(heap->objectsCount),
|
|
||||||
static_cast<KLong>(heap->totalObjectsSize));
|
|
||||||
}
|
}
|
||||||
if (extra) {
|
if (extra) {
|
||||||
add(builder, const_cast<KNativePtr>(static_cast<const void*>("extra")),
|
add(builder, extraPoolKey, static_cast<KLong>(extra->keptCount), static_cast<KLong>(extra->sweptCount));
|
||||||
static_cast<KLong>(extra->objectsCount),
|
|
||||||
static_cast<KLong>(extra->totalObjectsSize));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -66,7 +81,8 @@ struct GCInfo {
|
|||||||
std::optional<KLong> pauseEndTime;
|
std::optional<KLong> pauseEndTime;
|
||||||
std::optional<KLong> finalizersDoneTime;
|
std::optional<KLong> finalizersDoneTime;
|
||||||
std::optional<RootSetStatistics> rootSet;
|
std::optional<RootSetStatistics> rootSet;
|
||||||
std::optional<kotlin::gc::MemoryUsage> markStats;
|
std::optional<kotlin::gc::MarkStats> markStats;
|
||||||
|
SweepStatsMap sweepStats;
|
||||||
MemoryUsageMap memoryUsageBefore;
|
MemoryUsageMap memoryUsageBefore;
|
||||||
MemoryUsageMap memoryUsageAfter;
|
MemoryUsageMap memoryUsageAfter;
|
||||||
|
|
||||||
@@ -82,6 +98,9 @@ struct GCInfo {
|
|||||||
Kotlin_Internal_GC_GCInfoBuilder_setRootSet(
|
Kotlin_Internal_GC_GCInfoBuilder_setRootSet(
|
||||||
builder, rootSet->threadLocalReferences, rootSet->stackReferences, rootSet->globalReferences,
|
builder, rootSet->threadLocalReferences, rootSet->stackReferences, rootSet->globalReferences,
|
||||||
rootSet->stableReferences);
|
rootSet->stableReferences);
|
||||||
|
if (markStats)
|
||||||
|
Kotlin_Internal_GC_GCInfoBuilder_setMarkStats(builder, markStats->markedCount);
|
||||||
|
sweepStats.build(builder, Kotlin_Internal_GC_GCInfoBuilder_setSweepStats);
|
||||||
memoryUsageBefore.build(builder, Kotlin_Internal_GC_GCInfoBuilder_setMemoryUsageBefore);
|
memoryUsageBefore.build(builder, Kotlin_Internal_GC_GCInfoBuilder_setMemoryUsageBefore);
|
||||||
memoryUsageAfter.build(builder, Kotlin_Internal_GC_GCInfoBuilder_setMemoryUsageAfter);
|
memoryUsageAfter.build(builder, Kotlin_Internal_GC_GCInfoBuilder_setMemoryUsageAfter);
|
||||||
}
|
}
|
||||||
@@ -98,6 +117,12 @@ GCInfo* statByEpoch(uint64_t epoch) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MemoryUsage currentHeapUsage() noexcept {
|
||||||
|
return MemoryUsage{
|
||||||
|
mm::GlobalData::Instance().gc().GetTotalHeapObjectsSizeBytes(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
extern "C" void Kotlin_Internal_GC_GCInfoBuilder_Fill(KRef builder, int id) {
|
extern "C" void Kotlin_Internal_GC_GCInfoBuilder_Fill(KRef builder, int id) {
|
||||||
@@ -132,12 +157,23 @@ GCHandle GCHandle::create(uint64_t epoch) {
|
|||||||
} else {
|
} else {
|
||||||
GCLogInfo(epoch, "Started.");
|
GCLogInfo(epoch, "Started.");
|
||||||
}
|
}
|
||||||
|
current.memoryUsageBefore.heap = currentHeapUsage();
|
||||||
return getByEpoch(epoch);
|
return getByEpoch(epoch);
|
||||||
}
|
}
|
||||||
GCHandle GCHandle::createFakeForTests() { return getByEpoch(std::numeric_limits<uint64_t>::max()); }
|
GCHandle GCHandle::createFakeForTests() { return getByEpoch(std::numeric_limits<uint64_t>::max()); }
|
||||||
GCHandle GCHandle::getByEpoch(uint64_t epoch) {
|
GCHandle GCHandle::getByEpoch(uint64_t epoch) {
|
||||||
return GCHandle{epoch};
|
return GCHandle{epoch};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
std::optional<gc::GCHandle> gc::GCHandle::currentEpoch() noexcept {
|
||||||
|
std::lock_guard guard(lock);
|
||||||
|
if (auto epoch = current.epoch) {
|
||||||
|
return GCHandle::getByEpoch(*epoch);
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
void GCHandle::ClearForTests() {
|
void GCHandle::ClearForTests() {
|
||||||
std::lock_guard guard(lock);
|
std::lock_guard guard(lock);
|
||||||
current = {};
|
current = {};
|
||||||
@@ -147,6 +183,7 @@ void GCHandle::finished() {
|
|||||||
std::lock_guard guard(lock);
|
std::lock_guard guard(lock);
|
||||||
if (auto* stat = statByEpoch(epoch_)) {
|
if (auto* stat = statByEpoch(epoch_)) {
|
||||||
stat->endTime = static_cast<KLong>(konan::getTimeNanos());
|
stat->endTime = static_cast<KLong>(konan::getTimeNanos());
|
||||||
|
stat->memoryUsageAfter.heap = currentHeapUsage();
|
||||||
if (stat->rootSet) {
|
if (stat->rootSet) {
|
||||||
GCLogInfo(
|
GCLogInfo(
|
||||||
epoch_,
|
epoch_,
|
||||||
@@ -160,20 +197,22 @@ void GCHandle::finished() {
|
|||||||
stat->rootSet->stableReferences, stat->rootSet->total());
|
stat->rootSet->stableReferences, stat->rootSet->total());
|
||||||
}
|
}
|
||||||
if (stat->markStats) {
|
if (stat->markStats) {
|
||||||
GCLogInfo(epoch_, "Mark: %" PRIu64 " objects.", stat->markStats->objectsCount);
|
GCLogInfo(epoch_, "Mark: %" PRIu64 " objects.", stat->markStats->markedCount);
|
||||||
}
|
}
|
||||||
if (stat->memoryUsageAfter.extra && stat->memoryUsageBefore.extra) {
|
if (auto stats = stat->sweepStats.extra) {
|
||||||
GCLogInfo(
|
GCLogInfo(
|
||||||
epoch_, "Sweep extra objects: swept %" PRIu64 " objects, kept %" PRIu64 " objects",
|
epoch_, "Sweep extra objects: swept %" PRIu64 " objects, kept %" PRIu64 " objects",
|
||||||
stat->memoryUsageBefore.extra->objectsCount - stat->memoryUsageAfter.extra->objectsCount, stat->memoryUsageAfter.extra->objectsCount);
|
stats->sweptCount, stats->keptCount);
|
||||||
|
}
|
||||||
|
if (auto stats = stat->sweepStats.heap) {
|
||||||
|
GCLogInfo(
|
||||||
|
epoch_, "Sweep: swept %" PRIu64 " objects, kept %" PRIu64 " objects", stats->sweptCount,
|
||||||
|
stats->keptCount);
|
||||||
}
|
}
|
||||||
if (stat->memoryUsageBefore.heap && stat->memoryUsageAfter.heap) {
|
if (stat->memoryUsageBefore.heap && stat->memoryUsageAfter.heap) {
|
||||||
GCLogInfo(
|
GCLogInfo(
|
||||||
epoch_, "Sweep: swept %" PRIu64 " objects, kept %" PRIu64 " objects",
|
epoch_, "Heap memory usage: before %" PRIu64 " bytes, after %" PRIu64 " bytes", stat->memoryUsageBefore.heap->sizeBytes,
|
||||||
stat->memoryUsageBefore.heap->objectsCount - stat->memoryUsageAfter.heap->objectsCount, stat->memoryUsageAfter.heap->objectsCount);
|
stat->memoryUsageAfter.heap->sizeBytes);
|
||||||
GCLogInfo(
|
|
||||||
epoch_, "Heap memory usage: before %" PRIu64 " bytes, after %" PRIu64 " bytes", stat->memoryUsageBefore.heap->totalObjectsSize,
|
|
||||||
stat->memoryUsageAfter.heap->totalObjectsSize);
|
|
||||||
}
|
}
|
||||||
if (stat->pauseStartTime && stat->pauseEndTime) {
|
if (stat->pauseStartTime && stat->pauseEndTime) {
|
||||||
auto time = (*stat->pauseEndTime - *stat->pauseStartTime) / 1000;
|
auto time = (*stat->pauseEndTime - *stat->pauseStartTime) / 1000;
|
||||||
@@ -184,18 +223,6 @@ void GCHandle::finished() {
|
|||||||
GCLogInfo(epoch_, "Finished. Total GC epoch time is %" PRId64" microseconds.", time);
|
GCLogInfo(epoch_, "Finished. Total GC epoch time is %" PRId64" microseconds.", time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (auto usage = stat->memoryUsageAfter.heap; usage && stat->markStats) {
|
|
||||||
RuntimeAssert(
|
|
||||||
stat->markStats->objectsCount == usage->objectsCount,
|
|
||||||
"Mismatch in statistics: marked %" PRId64 " objects, while %" PRId64 " is alive after sweep",
|
|
||||||
stat->markStats->objectsCount, usage->objectsCount);
|
|
||||||
RuntimeAssert(
|
|
||||||
stat->markStats->totalObjectsSize == usage->totalObjectsSize,
|
|
||||||
"Mismatch in statistics: total marked size is %" PRId64 " bytes, while %" PRId64 " bytes is alive after sweep",
|
|
||||||
stat->markStats->totalObjectsSize, usage->totalObjectsSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stat == ¤t) {
|
if (stat == ¤t) {
|
||||||
last = current;
|
last = current;
|
||||||
current = {};
|
current = {};
|
||||||
@@ -218,6 +245,15 @@ void GCHandle::threadsAreSuspended() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (last.epoch) {
|
||||||
|
// Assisted sweeping from the last epoch must be completed before the check can be run.
|
||||||
|
if (last.markStats && last.sweepStats.heap) {
|
||||||
|
RuntimeAssert(
|
||||||
|
last.markStats->markedCount == last.sweepStats.heap->keptCount,
|
||||||
|
"Mismatch in statistics: marked %" PRId64 " objects, while %" PRId64 " are alive after sweep",
|
||||||
|
last.markStats->markedCount, last.sweepStats.heap->keptCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void GCHandle::threadsAreResumed() {
|
void GCHandle::threadsAreResumed() {
|
||||||
std::lock_guard guard(lock);
|
std::lock_guard guard(lock);
|
||||||
@@ -267,87 +303,73 @@ void GCHandle::globalRootSetCollected(uint64_t globalReferences, uint64_t stable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GCHandle::marked(kotlin::gc::MarkStats stats) {
|
||||||
void GCHandle::heapUsageBefore(MemoryUsage usage) {
|
|
||||||
std::lock_guard guard(lock);
|
|
||||||
if (auto* stat = statByEpoch(epoch_)) {
|
|
||||||
stat->memoryUsageBefore.heap = usage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GCHandle::marked(kotlin::gc::MemoryUsage usage) {
|
|
||||||
std::lock_guard guard(lock);
|
std::lock_guard guard(lock);
|
||||||
if (auto* stat = statByEpoch(epoch_)) {
|
if (auto* stat = statByEpoch(epoch_)) {
|
||||||
if (!stat->markStats) {
|
if (!stat->markStats) {
|
||||||
stat->markStats = MemoryUsage{0, 0};
|
stat->markStats = MarkStats{};
|
||||||
}
|
}
|
||||||
stat->markStats->totalObjectsSize += usage.totalObjectsSize;
|
stat->markStats->markedSizeBytes += stats.markedSizeBytes;
|
||||||
stat->markStats->objectsCount += usage.objectsCount;
|
stat->markStats->markedCount += stats.markedCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryUsage GCHandle::getMarked() {
|
MarkStats GCHandle::getMarked() {
|
||||||
std::lock_guard guard(lock);
|
std::lock_guard guard(lock);
|
||||||
if (auto* stat = statByEpoch(epoch_)) {
|
if (auto* stat = statByEpoch(epoch_)) {
|
||||||
if (stat->markStats) {
|
if (stat->markStats) {
|
||||||
return *stat->markStats;
|
return *stat->markStats;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return MemoryUsage{0, 0};
|
return MarkStats{};
|
||||||
}
|
}
|
||||||
|
|
||||||
void GCHandle::heapUsageAfter(MemoryUsage usage) {
|
void GCHandle::swept(gc::SweepStats stats) noexcept {
|
||||||
std::lock_guard guard(lock);
|
std::lock_guard guard(lock);
|
||||||
if (auto* stat = statByEpoch(epoch_)) {
|
if (auto* stat = statByEpoch(epoch_)) {
|
||||||
stat->memoryUsageAfter.heap = usage;
|
auto& heap = stat->sweepStats.heap;
|
||||||
|
if (!heap) {
|
||||||
|
heap = gc::SweepStats{};
|
||||||
|
}
|
||||||
|
heap->keptCount += stats.keptCount;
|
||||||
|
heap->sweptCount += stats.sweptCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GCHandle::extraObjectsUsageBefore(MemoryUsage usage) {
|
void GCHandle::sweptExtraObjects(gc::SweepStats stats, uint64_t markedCount) noexcept {
|
||||||
std::lock_guard guard(lock);
|
std::lock_guard guard(lock);
|
||||||
if (auto* stat = statByEpoch(epoch_)) {
|
if (auto* stat = statByEpoch(epoch_)) {
|
||||||
stat->memoryUsageBefore.extra = usage;
|
auto& extra = stat->sweepStats.extra;
|
||||||
}
|
if (!extra) {
|
||||||
}
|
extra = gc::SweepStats{};
|
||||||
void GCHandle::extraObjectsUsageAfter(MemoryUsage usage) {
|
}
|
||||||
std::lock_guard guard(lock);
|
extra->keptCount += stats.keptCount;
|
||||||
if (auto* stat = statByEpoch(epoch_)) {
|
extra->sweptCount += stats.sweptCount;
|
||||||
stat->memoryUsageAfter.extra = usage;
|
RuntimeAssert(static_cast<bool>(stat->markStats), "Mark must have already happened");
|
||||||
|
stat->markStats->markedCount += markedCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryUsage GCHandle::GCSweepScope::getUsage() {
|
GCHandle::GCSweepScope::GCSweepScope(kotlin::gc::GCHandle& handle) : handle_(handle) {}
|
||||||
return MemoryUsage{
|
|
||||||
mm::GlobalData::Instance().gc().GetHeapObjectsCountUnsafe(),
|
|
||||||
mm::GlobalData::Instance().gc().GetTotalHeapObjectsSizeUnsafe(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
GCHandle::GCSweepScope::GCSweepScope(kotlin::gc::GCHandle& handle) :
|
|
||||||
handle_(handle) {
|
|
||||||
handle_.heapUsageBefore(getUsage());
|
|
||||||
}
|
|
||||||
|
|
||||||
GCHandle::GCSweepScope::~GCSweepScope() {
|
GCHandle::GCSweepScope::~GCSweepScope() {
|
||||||
GCLogDebug(handle_.getEpoch(), "Swept is done in %" PRIu64 " microseconds.", getStageTime());
|
handle_.swept(stats_);
|
||||||
handle_.heapUsageAfter(getUsage());
|
GCLogDebug(
|
||||||
|
handle_.getEpoch(),
|
||||||
|
"Collected %" PRId64 " heap objects in %" PRIu64 " microseconds. "
|
||||||
|
"%" PRId64 " heap objects are kept alive.",
|
||||||
|
stats_.sweptCount, getStageTime(), stats_.keptCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryUsage GCHandle::GCSweepExtraObjectsScope::getUsage() {
|
GCHandle::GCSweepExtraObjectsScope::GCSweepExtraObjectsScope(kotlin::gc::GCHandle& handle) : handle_(handle) {}
|
||||||
return MemoryUsage{
|
|
||||||
mm::GlobalData::Instance().gc().GetExtraObjectsCountUnsafe(),
|
|
||||||
mm::GlobalData::Instance().gc().GetTotalExtraObjectsSizeUnsafe(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
GCHandle::GCSweepExtraObjectsScope::GCSweepExtraObjectsScope(kotlin::gc::GCHandle& handle) :
|
|
||||||
handle_(handle) {
|
|
||||||
handle_.extraObjectsUsageBefore(getUsage());
|
|
||||||
}
|
|
||||||
|
|
||||||
GCHandle::GCSweepExtraObjectsScope::~GCSweepExtraObjectsScope() {
|
GCHandle::GCSweepExtraObjectsScope::~GCSweepExtraObjectsScope() {
|
||||||
GCLogDebug(handle_.getEpoch(), "Swept extra objects is done in %" PRIu64 " microseconds", getStageTime());
|
handle_.sweptExtraObjects(stats_, markedCount_);
|
||||||
handle_.extraObjectsUsageAfter(getUsage());
|
GCLogDebug(
|
||||||
|
handle_.getEpoch(),
|
||||||
|
"Collected %" PRId64 " extra objects in %" PRIu64 " microseconds. "
|
||||||
|
"%" PRId64 " extra objects are kept alive.",
|
||||||
|
stats_.sweptCount, getStageTime(), stats_.keptCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
GCHandle::GCGlobalRootSetScope::GCGlobalRootSetScope(kotlin::gc::GCHandle& handle) : handle_(handle) {}
|
GCHandle::GCGlobalRootSetScope::GCGlobalRootSetScope(kotlin::gc::GCHandle& handle) : handle_(handle) {}
|
||||||
@@ -370,10 +392,8 @@ GCHandle::GCThreadRootSetScope::~GCThreadRootSetScope(){
|
|||||||
GCHandle::GCMarkScope::GCMarkScope(kotlin::gc::GCHandle& handle) : handle_(handle){}
|
GCHandle::GCMarkScope::GCMarkScope(kotlin::gc::GCHandle& handle) : handle_(handle){}
|
||||||
|
|
||||||
GCHandle::GCMarkScope::~GCMarkScope() {
|
GCHandle::GCMarkScope::~GCMarkScope() {
|
||||||
handle_.marked(MemoryUsage{objectsCount, totalObjectSizeBytes});
|
handle_.marked(stats_);
|
||||||
GCLogDebug(handle_.getEpoch(),
|
GCLogDebug(handle_.getEpoch(), "Marked %" PRIu64 " objects in %" PRIu64 " microseconds.", stats_.markedCount, getStageTime());
|
||||||
"Marked %" PRIu64 " objects in %" PRIu64 " microseconds",
|
|
||||||
objectsCount, getStageTime());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gc::GCHandle::GCProcessWeaksScope::GCProcessWeaksScope(gc::GCHandle& handle) noexcept : handle_(handle) {}
|
gc::GCHandle::GCProcessWeaksScope::GCProcessWeaksScope(gc::GCHandle& handle) noexcept : handle_(handle) {}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include "Porting.h"
|
#include "Porting.h"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
|
#include "std_support/Optional.hpp"
|
||||||
|
|
||||||
#define GCLogInfo(epoch, format, ...) RuntimeLogInfo({kTagGC}, "Epoch #%" PRIu64 ": " format, epoch, ##__VA_ARGS__)
|
#define GCLogInfo(epoch, format, ...) RuntimeLogInfo({kTagGC}, "Epoch #%" PRIu64 ": " format, epoch, ##__VA_ARGS__)
|
||||||
#define GCLogDebug(epoch, format, ...) RuntimeLogDebug({kTagGC}, "Epoch #%" PRIu64 ": " format, epoch, ##__VA_ARGS__)
|
#define GCLogDebug(epoch, format, ...) RuntimeLogDebug({kTagGC}, "Epoch #%" PRIu64 ": " format, epoch, ##__VA_ARGS__)
|
||||||
@@ -24,9 +25,14 @@ namespace kotlin::gc {
|
|||||||
|
|
||||||
class GCHandle;
|
class GCHandle;
|
||||||
|
|
||||||
struct MemoryUsage {
|
struct SweepStats {
|
||||||
uint64_t objectsCount;
|
uint64_t sweptCount = 0;
|
||||||
uint64_t totalObjectsSize;
|
uint64_t keptCount = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MarkStats {
|
||||||
|
uint64_t markedCount = 0;
|
||||||
|
uint64_t markedSizeBytes = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class GCHandle {
|
class GCHandle {
|
||||||
@@ -39,20 +45,29 @@ public:
|
|||||||
|
|
||||||
class GCSweepScope : GCStageScopeUsTimer, Pinned {
|
class GCSweepScope : GCStageScopeUsTimer, Pinned {
|
||||||
GCHandle& handle_;
|
GCHandle& handle_;
|
||||||
MemoryUsage getUsage();
|
SweepStats stats_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit GCSweepScope(GCHandle& handle);
|
explicit GCSweepScope(GCHandle& handle);
|
||||||
~GCSweepScope();
|
~GCSweepScope();
|
||||||
|
|
||||||
|
void addSweptObject() noexcept { stats_.sweptCount += 1; }
|
||||||
|
void addKeptObject() noexcept { stats_.keptCount += 1; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class GCSweepExtraObjectsScope : GCStageScopeUsTimer, Pinned {
|
class GCSweepExtraObjectsScope : GCStageScopeUsTimer, Pinned {
|
||||||
GCHandle& handle_;
|
GCHandle& handle_;
|
||||||
MemoryUsage getUsage();
|
SweepStats stats_;
|
||||||
|
uint64_t markedCount_ = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit GCSweepExtraObjectsScope(GCHandle& handle);
|
explicit GCSweepExtraObjectsScope(GCHandle& handle);
|
||||||
~GCSweepExtraObjectsScope();
|
~GCSweepExtraObjectsScope();
|
||||||
|
|
||||||
|
void addSweptObject() noexcept { stats_.sweptCount += 1; }
|
||||||
|
void addKeptObject() noexcept { stats_.keptCount += 1; }
|
||||||
|
// Custom allocator only. To be finalized objects are kept alive.
|
||||||
|
void addMarkedObject() noexcept { markedCount_ += 1; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class GCGlobalRootSetScope : GCStageScopeUsTimer, Pinned {
|
class GCGlobalRootSetScope : GCStageScopeUsTimer, Pinned {
|
||||||
@@ -82,15 +97,15 @@ public:
|
|||||||
|
|
||||||
class GCMarkScope : GCStageScopeUsTimer, Pinned {
|
class GCMarkScope : GCStageScopeUsTimer, Pinned {
|
||||||
GCHandle& handle_;
|
GCHandle& handle_;
|
||||||
uint64_t objectsCount = 0;
|
MarkStats stats_;
|
||||||
uint64_t totalObjectSizeBytes = 0;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit GCMarkScope(GCHandle& handle);
|
explicit GCMarkScope(GCHandle& handle);
|
||||||
~GCMarkScope();
|
~GCMarkScope();
|
||||||
void addObject(uint64_t objectSize) {
|
|
||||||
totalObjectSizeBytes += objectSize;
|
void addObject(uint64_t objectSize) noexcept {
|
||||||
objectsCount++;
|
++stats_.markedCount;
|
||||||
|
stats_.markedSizeBytes += objectSize;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -115,16 +130,15 @@ private:
|
|||||||
|
|
||||||
void threadRootSetCollected(mm::ThreadData& threadData, uint64_t threadLocalReferences, uint64_t stackReferences);
|
void threadRootSetCollected(mm::ThreadData& threadData, uint64_t threadLocalReferences, uint64_t stackReferences);
|
||||||
void globalRootSetCollected(uint64_t globalReferences, uint64_t stableReferences);
|
void globalRootSetCollected(uint64_t globalReferences, uint64_t stableReferences);
|
||||||
void heapUsageBefore(MemoryUsage usage);
|
void swept(SweepStats stats) noexcept;
|
||||||
void heapUsageAfter(MemoryUsage usage);
|
void sweptExtraObjects(SweepStats stats, uint64_t markedCount) noexcept;
|
||||||
void extraObjectsUsageBefore(MemoryUsage usage);
|
void marked(MarkStats stats);
|
||||||
void extraObjectsUsageAfter(MemoryUsage usage);
|
|
||||||
void marked(MemoryUsage usage);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static GCHandle create(uint64_t epoch);
|
static GCHandle create(uint64_t epoch);
|
||||||
static GCHandle createFakeForTests();
|
static GCHandle createFakeForTests();
|
||||||
static GCHandle getByEpoch(uint64_t epoch);
|
static GCHandle getByEpoch(uint64_t epoch);
|
||||||
|
static std::optional<GCHandle> currentEpoch() noexcept;
|
||||||
static void ClearForTests();
|
static void ClearForTests();
|
||||||
|
|
||||||
uint64_t getEpoch() { return epoch_; }
|
uint64_t getEpoch() { return epoch_; }
|
||||||
@@ -141,6 +155,6 @@ public:
|
|||||||
GCMarkScope mark() { return GCMarkScope(*this); }
|
GCMarkScope mark() { return GCMarkScope(*this); }
|
||||||
GCProcessWeaksScope processWeaks() noexcept { return GCProcessWeaksScope(*this); }
|
GCProcessWeaksScope processWeaks() noexcept { return GCProcessWeaksScope(*this); }
|
||||||
|
|
||||||
MemoryUsage getMarked();
|
MarkStats getMarked();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,12 +118,15 @@ void SweepExtraObjects(GCHandle handle, typename Traits::ExtraObjectsFactory::It
|
|||||||
if (extraObject.HasAssociatedObject()) {
|
if (extraObject.HasAssociatedObject()) {
|
||||||
extraObject.setFlag(mm::ExtraObjectData::FLAGS_IN_FINALIZER_QUEUE);
|
extraObject.setFlag(mm::ExtraObjectData::FLAGS_IN_FINALIZER_QUEUE);
|
||||||
++it;
|
++it;
|
||||||
|
sweepHandle.addKeptObject();
|
||||||
} else {
|
} else {
|
||||||
extraObject.Uninstall();
|
extraObject.Uninstall();
|
||||||
it.EraseAndAdvance();
|
it.EraseAndAdvance();
|
||||||
|
sweepHandle.addSweptObject();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
++it;
|
++it;
|
||||||
|
sweepHandle.addKeptObject();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,8 +145,10 @@ typename Traits::ObjectFactory::FinalizerQueue Sweep(GCHandle handle, typename T
|
|||||||
for (auto it = objectFactoryIter.begin(); it != objectFactoryIter.end();) {
|
for (auto it = objectFactoryIter.begin(); it != objectFactoryIter.end();) {
|
||||||
if (Traits::TryResetMark(*it)) {
|
if (Traits::TryResetMark(*it)) {
|
||||||
++it;
|
++it;
|
||||||
|
sweepHandle.addKeptObject();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
sweepHandle.addSweptObject();
|
||||||
auto* objHeader = it->GetObjHeader();
|
auto* objHeader = it->GetObjHeader();
|
||||||
if (HasFinalizers(objHeader)) {
|
if (HasFinalizers(objHeader)) {
|
||||||
objectFactoryIter.MoveAndAdvance(finalizerQueue, it);
|
objectFactoryIter.MoveAndAdvance(finalizerQueue, it);
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ public:
|
|||||||
return testing::UnorderedElementsAreArray(objects);
|
return testing::UnorderedElementsAreArray(objects);
|
||||||
}
|
}
|
||||||
|
|
||||||
gc::MemoryUsage Mark(std::initializer_list<std::reference_wrapper<test_support::Any>> graySet) {
|
gc::MarkStats Mark(std::initializer_list<std::reference_wrapper<test_support::Any>> graySet) {
|
||||||
std_support::vector<ObjHeader*> objects;
|
std_support::vector<ObjHeader*> objects;
|
||||||
for (auto& object : graySet) ScopedMarkTraits::tryEnqueue(objects, object.get().header());
|
for (auto& object : graySet) ScopedMarkTraits::tryEnqueue(objects, object.get().header());
|
||||||
auto handle = gc::GCHandle::create(epoch_++);
|
auto handle = gc::GCHandle::create(epoch_++);
|
||||||
@@ -168,8 +168,8 @@ size_t GetObjectsSize(std::initializer_list<std::reference_wrapper<test_support:
|
|||||||
#define EXPECT_MARKED(stats, ...) \
|
#define EXPECT_MARKED(stats, ...) \
|
||||||
do { \
|
do { \
|
||||||
std::initializer_list<std::reference_wrapper<test_support::Any>> objects = {__VA_ARGS__}; \
|
std::initializer_list<std::reference_wrapper<test_support::Any>> objects = {__VA_ARGS__}; \
|
||||||
EXPECT_THAT(stats.objectsCount, objects.size()); \
|
EXPECT_THAT(stats.markedCount, objects.size()); \
|
||||||
EXPECT_THAT(stats.totalObjectsSize, GetObjectsSize(objects)); \
|
EXPECT_THAT(stats.markedSizeBytes, GetObjectsSize(objects)); \
|
||||||
EXPECT_THAT(marked(), MarkedMatcher(objects)); \
|
EXPECT_THAT(marked(), MarkedMatcher(objects)); \
|
||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
|
|||||||
@@ -67,19 +67,6 @@ size_t gc::GC::GetAllocatedHeapSize(ObjHeader* object) noexcept {
|
|||||||
return mm::ObjectFactory<GCImpl>::GetAllocatedHeapSize(object);
|
return mm::ObjectFactory<GCImpl>::GetAllocatedHeapSize(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t gc::GC::GetHeapObjectsCountUnsafe() const noexcept {
|
|
||||||
return impl_->objectFactory().GetObjectsCountUnsafe();
|
|
||||||
}
|
|
||||||
size_t gc::GC::GetTotalHeapObjectsSizeUnsafe() const noexcept {
|
|
||||||
return impl_->objectFactory().GetTotalObjectsSizeUnsafe();
|
|
||||||
}
|
|
||||||
size_t gc::GC::GetExtraObjectsCountUnsafe() const noexcept {
|
|
||||||
return mm::GlobalData::Instance().extraObjectDataFactory().GetSizeUnsafe();
|
|
||||||
}
|
|
||||||
size_t gc::GC::GetTotalExtraObjectsSizeUnsafe() const noexcept {
|
|
||||||
return mm::GlobalData::Instance().extraObjectDataFactory().GetTotalObjectsSizeUnsafe();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t gc::GC::GetTotalHeapObjectsSizeBytes() const noexcept {
|
size_t gc::GC::GetTotalHeapObjectsSizeBytes() const noexcept {
|
||||||
return allocatedBytes();
|
return allocatedBytes();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,19 +80,6 @@ size_t gc::GC::GetAllocatedHeapSize(ObjHeader* object) noexcept {
|
|||||||
return mm::ObjectFactory<GCImpl>::GetAllocatedHeapSize(object);
|
return mm::ObjectFactory<GCImpl>::GetAllocatedHeapSize(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t gc::GC::GetHeapObjectsCountUnsafe() const noexcept {
|
|
||||||
return impl_->objectFactory().GetObjectsCountUnsafe();
|
|
||||||
}
|
|
||||||
size_t gc::GC::GetTotalHeapObjectsSizeUnsafe() const noexcept {
|
|
||||||
return impl_->objectFactory().GetTotalObjectsSizeUnsafe();
|
|
||||||
}
|
|
||||||
size_t gc::GC::GetExtraObjectsCountUnsafe() const noexcept {
|
|
||||||
return mm::GlobalData::Instance().extraObjectDataFactory().GetSizeUnsafe();
|
|
||||||
}
|
|
||||||
size_t gc::GC::GetTotalExtraObjectsSizeUnsafe() const noexcept {
|
|
||||||
return mm::GlobalData::Instance().extraObjectDataFactory().GetTotalObjectsSizeUnsafe();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t gc::GC::GetTotalHeapObjectsSizeBytes() const noexcept {
|
size_t gc::GC::GetTotalHeapObjectsSizeBytes() const noexcept {
|
||||||
return allocatedBytes();
|
return allocatedBytes();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ bool gc::SameThreadMarkAndSweep::PerformFullGC() noexcept {
|
|||||||
|
|
||||||
gc::Mark<internal::MarkTraits>(gcHandle, markQueue_);
|
gc::Mark<internal::MarkTraits>(gcHandle, markQueue_);
|
||||||
auto markStats = gcHandle.getMarked();
|
auto markStats = gcHandle.getMarked();
|
||||||
scheduler.gcData().UpdateAliveSetBytes(markStats.totalObjectsSize);
|
scheduler.gcData().UpdateAliveSetBytes(markStats.markedSizeBytes);
|
||||||
|
|
||||||
gc::processWeaks<ProcessWeaksTraits>(gcHandle, mm::SpecialRefRegistry::instance());
|
gc::processWeaks<ProcessWeaksTraits>(gcHandle, mm::SpecialRefRegistry::instance());
|
||||||
|
|
||||||
|
|||||||
@@ -102,13 +102,13 @@ public class GCInfo(
|
|||||||
},
|
},
|
||||||
info.memoryUsageBefore.mapValues { (_, v) ->
|
info.memoryUsageBefore.mapValues { (_, v) ->
|
||||||
MemoryUsage(
|
MemoryUsage(
|
||||||
v.objectsCount,
|
0L,
|
||||||
v.totalObjectsSizeBytes,
|
v.totalObjectsSizeBytes,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
info.memoryUsageAfter.mapValues { (_, v) ->
|
info.memoryUsageAfter.mapValues { (_, v) ->
|
||||||
MemoryUsage(
|
MemoryUsage(
|
||||||
v.objectsCount,
|
0L,
|
||||||
v.totalObjectsSizeBytes,
|
v.totalObjectsSizeBytes,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import kotlin.system.*
|
|||||||
/**
|
/**
|
||||||
* This class represents statistics of memory usage in one memory pool.
|
* This class represents statistics of memory usage in one memory pool.
|
||||||
*
|
*
|
||||||
* @property objectsCount The number of allocated objects.
|
|
||||||
* @property totalObjectsSizeBytes The total size of allocated objects. System allocator overhead is not included,
|
* @property totalObjectsSizeBytes The total size of allocated objects. System allocator overhead is not included,
|
||||||
* so it can not perfectly match the value received by os tools.
|
* so it can not perfectly match the value received by os tools.
|
||||||
* All alignment and auxiliary object headers are included.
|
* All alignment and auxiliary object headers are included.
|
||||||
@@ -24,10 +23,22 @@ import kotlin.system.*
|
|||||||
@NativeRuntimeApi
|
@NativeRuntimeApi
|
||||||
@SinceKotlin("1.9")
|
@SinceKotlin("1.9")
|
||||||
public class MemoryUsage(
|
public class MemoryUsage(
|
||||||
val objectsCount: Long,
|
|
||||||
val totalObjectsSizeBytes: Long,
|
val totalObjectsSizeBytes: Long,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents statistics of sweeping in one memory pool.
|
||||||
|
*
|
||||||
|
* @property sweptCount The of objects that were freed.
|
||||||
|
* @property keptCount The number of objects that were processed but kept alive.
|
||||||
|
*/
|
||||||
|
@NativeRuntimeApi
|
||||||
|
@SinceKotlin("1.9")
|
||||||
|
public class SweepStatistics(
|
||||||
|
val sweptCount: Long,
|
||||||
|
val keptCount: Long,
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents statistics of the root set for garbage collector run, separated by root set pools.
|
* This class represents statistics of the root set for garbage collector run, separated by root set pools.
|
||||||
* These nodes are assumed to be used, even if there are no references for them.
|
* These nodes are assumed to be used, even if there are no references for them.
|
||||||
@@ -64,12 +75,16 @@ public class RootSetStatistics(
|
|||||||
* @property postGcCleanupTimeNs Time, when all memory is reclaimed, measured by [kotlin.system.getTimeNanos].
|
* @property postGcCleanupTimeNs Time, when all memory is reclaimed, measured by [kotlin.system.getTimeNanos].
|
||||||
* If null, memory reclamation is still in progress.
|
* If null, memory reclamation is still in progress.
|
||||||
* @property rootSet The number of objects in each root set pool. Check [RootSetStatistics] doc for details.
|
* @property rootSet The number of objects in each root set pool. Check [RootSetStatistics] doc for details.
|
||||||
|
* @property markedCount How many objects were processed during marking phase.
|
||||||
|
* @property sweepStatistics Sweeping statistics separated by memory pools.
|
||||||
|
* The set of memory pools depends on the collector implementation.
|
||||||
|
* Can be empty, if collection is in progress.
|
||||||
* @property memoryUsageAfter Memory usage at the start of garbage collector run, separated by memory pools.
|
* @property memoryUsageAfter Memory usage at the start of garbage collector run, separated by memory pools.
|
||||||
* The set of memory pools depends on the collector implementation.
|
* The set of memory pools depends on the collector implementation.
|
||||||
* Can be empty, of colelction is in progress.
|
* Can be empty, if collection is in progress.
|
||||||
* @property memoryUsageBefore Memory usage at the end of garbage collector run, separated by memory pools.
|
* @property memoryUsageBefore Memory usage at the end of garbage collector run, separated by memory pools.
|
||||||
* The set of memory pools depends on the collector implementation.
|
* The set of memory pools depends on the collector implementation.
|
||||||
* Can be empty, of colelction is in progress.
|
* Can be empty, if collection is in progress.
|
||||||
*/
|
*/
|
||||||
@NativeRuntimeApi
|
@NativeRuntimeApi
|
||||||
@SinceKotlin("1.9")
|
@SinceKotlin("1.9")
|
||||||
@@ -81,6 +96,8 @@ public class GCInfo(
|
|||||||
val pauseEndTimeNs: Long,
|
val pauseEndTimeNs: Long,
|
||||||
val postGcCleanupTimeNs: Long?,
|
val postGcCleanupTimeNs: Long?,
|
||||||
val rootSet: RootSetStatistics,
|
val rootSet: RootSetStatistics,
|
||||||
|
val markedCount: Long,
|
||||||
|
val sweepStatistics: Map<String, SweepStatistics>,
|
||||||
val memoryUsageBefore: Map<String, MemoryUsage>,
|
val memoryUsageBefore: Map<String, MemoryUsage>,
|
||||||
val memoryUsageAfter: Map<String, MemoryUsage>,
|
val memoryUsageAfter: Map<String, MemoryUsage>,
|
||||||
) {
|
) {
|
||||||
@@ -102,6 +119,8 @@ private class GCInfoBuilder() {
|
|||||||
var pauseEndTimeNs: Long? = null
|
var pauseEndTimeNs: Long? = null
|
||||||
var postGcCleanupTimeNs: Long? = null
|
var postGcCleanupTimeNs: Long? = null
|
||||||
var rootSet: RootSetStatistics? = null
|
var rootSet: RootSetStatistics? = null
|
||||||
|
var markedCount: Long? = null
|
||||||
|
var sweepStatistics = mutableMapOf<String, SweepStatistics>()
|
||||||
var memoryUsageBefore = mutableMapOf<String, MemoryUsage>()
|
var memoryUsageBefore = mutableMapOf<String, MemoryUsage>()
|
||||||
var memoryUsageAfter = mutableMapOf<String, MemoryUsage>()
|
var memoryUsageAfter = mutableMapOf<String, MemoryUsage>()
|
||||||
|
|
||||||
@@ -140,17 +159,29 @@ private class GCInfoBuilder() {
|
|||||||
rootSet = RootSetStatistics(threadLocalReferences, stackReferences, globalReferences, stableReferences)
|
rootSet = RootSetStatistics(threadLocalReferences, stackReferences, globalReferences, stableReferences)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExportForCppRuntime("Kotlin_Internal_GC_GCInfoBuilder_setMemoryUsageBefore")
|
@ExportForCppRuntime("Kotlin_Internal_GC_GCInfoBuilder_setMarkStats")
|
||||||
private fun setMemoryUsageBefore(name: NativePtr, objectsCount: Long, totalObjectsSize: Long) {
|
private fun setMarkStats(markedCount: Long) {
|
||||||
|
this.markedCount = markedCount
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExportForCppRuntime("Kotlin_Internal_GC_GCInfoBuilder_setSweepStats")
|
||||||
|
private fun setSweepStats(name: NativePtr, keptCount: Long, sweptCount: Long) {
|
||||||
val nameString = interpretCPointer<ByteVar>(name)!!.toKString()
|
val nameString = interpretCPointer<ByteVar>(name)!!.toKString()
|
||||||
val memoryUsage = MemoryUsage(objectsCount, totalObjectsSize)
|
val stats = SweepStatistics(sweptCount, keptCount)
|
||||||
|
sweepStatistics[nameString] = stats
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExportForCppRuntime("Kotlin_Internal_GC_GCInfoBuilder_setMemoryUsageBefore")
|
||||||
|
private fun setMemoryUsageBefore(name: NativePtr, totalObjectsSize: Long) {
|
||||||
|
val nameString = interpretCPointer<ByteVar>(name)!!.toKString()
|
||||||
|
val memoryUsage = MemoryUsage(totalObjectsSize)
|
||||||
memoryUsageBefore[nameString] = memoryUsage
|
memoryUsageBefore[nameString] = memoryUsage
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExportForCppRuntime("Kotlin_Internal_GC_GCInfoBuilder_setMemoryUsageAfter")
|
@ExportForCppRuntime("Kotlin_Internal_GC_GCInfoBuilder_setMemoryUsageAfter")
|
||||||
private fun setMemoryUsageAfter(name: NativePtr, objectsCount: Long, totalObjectsSize: Long) {
|
private fun setMemoryUsageAfter(name: NativePtr, totalObjectsSize: Long) {
|
||||||
val nameString = interpretCPointer<ByteVar>(name)!!.toKString()
|
val nameString = interpretCPointer<ByteVar>(name)!!.toKString()
|
||||||
val memoryUsage = MemoryUsage(objectsCount, totalObjectsSize)
|
val memoryUsage = MemoryUsage(totalObjectsSize)
|
||||||
memoryUsageAfter[nameString] = memoryUsage
|
memoryUsageAfter[nameString] = memoryUsage
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,6 +194,8 @@ private class GCInfoBuilder() {
|
|||||||
pauseEndTimeNs ?: return null,
|
pauseEndTimeNs ?: return null,
|
||||||
postGcCleanupTimeNs,
|
postGcCleanupTimeNs,
|
||||||
rootSet ?: return null,
|
rootSet ?: return null,
|
||||||
|
markedCount ?: return null,
|
||||||
|
sweepStatistics.toMap(),
|
||||||
memoryUsageBefore.toMap(),
|
memoryUsageBefore.toMap(),
|
||||||
memoryUsageAfter.toMap()
|
memoryUsageAfter.toMap()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ public:
|
|||||||
void ClearForTests() noexcept { extraObjects_.ClearForTests(); }
|
void ClearForTests() noexcept { extraObjects_.ClearForTests(); }
|
||||||
|
|
||||||
size_t GetSizeUnsafe() noexcept { return extraObjects_.GetSizeUnsafe(); }
|
size_t GetSizeUnsafe() noexcept { return extraObjects_.GetSizeUnsafe(); }
|
||||||
size_t GetTotalObjectsSizeUnsafe() noexcept { return extraObjects_.GetSizeUnsafe() * sizeof(ExtraObjectData); }
|
|
||||||
|
|
||||||
// requires LockForIter
|
// requires LockForIter
|
||||||
void EraseAndAdvance(Iterator &it) { extraObjects_.EraseAndAdvance(it); }
|
void EraseAndAdvance(Iterator &it) { extraObjects_.EraseAndAdvance(it); }
|
||||||
|
|||||||
@@ -390,7 +390,6 @@ public:
|
|||||||
Iterable LockForIter() noexcept { return Iterable(*this); }
|
Iterable LockForIter() noexcept { return Iterable(*this); }
|
||||||
|
|
||||||
size_t GetSizeUnsafe() const noexcept { return size_; }
|
size_t GetSizeUnsafe() const noexcept { return size_; }
|
||||||
size_t GetTotalObjectsSizeUnsafe() const noexcept { return totalObjectsSizeBytes_; }
|
|
||||||
|
|
||||||
void ClearForTests() {
|
void ClearForTests() {
|
||||||
root_.reset();
|
root_.reset();
|
||||||
@@ -701,9 +700,6 @@ public:
|
|||||||
// Lock ObjectFactory for safe iteration.
|
// Lock ObjectFactory for safe iteration.
|
||||||
Iterable LockForIter() noexcept { return Iterable(*this); }
|
Iterable LockForIter() noexcept { return Iterable(*this); }
|
||||||
|
|
||||||
size_t GetObjectsCountUnsafe() const noexcept { return storage_.GetSizeUnsafe(); }
|
|
||||||
size_t GetTotalObjectsSizeUnsafe() const noexcept { return storage_.GetTotalObjectsSizeUnsafe(); }
|
|
||||||
|
|
||||||
void ClearForTests() { storage_.ClearForTests(); }
|
void ClearForTests() { storage_.ClearForTests(); }
|
||||||
|
|
||||||
static size_t GetAllocatedHeapSize(ObjHeader* object) noexcept {
|
static size_t GetAllocatedHeapSize(ObjHeader* object) noexcept {
|
||||||
|
|||||||
@@ -249,6 +249,12 @@ void Kotlin_Internal_GC_GCInfoBuilder_setPostGcCleanupTime(KRef thiz, KLong valu
|
|||||||
void Kotlin_Internal_GC_GCInfoBuilder_setRootSet(KRef thiz, KLong threadLocalReferences, KLong stackReferences, KLong globalReferences, KLong stableReferences) {
|
void Kotlin_Internal_GC_GCInfoBuilder_setRootSet(KRef thiz, KLong threadLocalReferences, KLong stackReferences, KLong globalReferences, KLong stableReferences) {
|
||||||
throw std::runtime_error("Not implemented for tests");
|
throw std::runtime_error("Not implemented for tests");
|
||||||
}
|
}
|
||||||
|
void Kotlin_Internal_GC_GCInfoBuilder_setMarkStats(KRef thiz, KLong markedCount) {
|
||||||
|
throw std::runtime_error("Not implemented for tests");
|
||||||
|
}
|
||||||
|
void Kotlin_Internal_GC_GCInfoBuilder_setSweepStats(KRef thiz, KNativePtr name, KLong sweptCount, KLong keptCount) {
|
||||||
|
throw std::runtime_error("Not implemented for tests");
|
||||||
|
}
|
||||||
void Kotlin_Internal_GC_GCInfoBuilder_setMemoryUsageBefore(KRef thiz, KNativePtr name, KLong objectsCount, KLong totalObjectsSize) {
|
void Kotlin_Internal_GC_GCInfoBuilder_setMemoryUsageBefore(KRef thiz, KNativePtr name, KLong objectsCount, KLong totalObjectsSize) {
|
||||||
throw std::runtime_error("Not implemented for tests");
|
throw std::runtime_error("Not implemented for tests");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user