From 353c503c165d5584dbe5659cca51bd01b12b51b8 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Tue, 11 Oct 2022 13:26:42 +0200 Subject: [PATCH 1/2] Increase size of frozen segments linearly --- src/coreclr/vm/frozenobjectheap.cpp | 41 ++++++++++++++++++++--------- src/coreclr/vm/frozenobjectheap.h | 10 ++++++- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/coreclr/vm/frozenobjectheap.cpp b/src/coreclr/vm/frozenobjectheap.cpp index bef82ba54502e..66dbf4803a281 100644 --- a/src/coreclr/vm/frozenobjectheap.cpp +++ b/src/coreclr/vm/frozenobjectheap.cpp @@ -4,8 +4,8 @@ #include "common.h" #include "frozenobjectheap.h" -// Size to reserve for a frozen segment -#define FOH_SEGMENT_SIZE (4 * 1024 * 1024) +// Default size to reserve for a frozen segment +#define FOH_SEGMENT_DEFAULT_SIZE (4 * 1024 * 1024) // Size to commit on demand in that reserved space #define FOH_COMMIT_SIZE (64 * 1024) @@ -36,8 +36,6 @@ Object* FrozenObjectHeapManager::TryAllocateObject(PTR_MethodTable type, size_t _ASSERT(type != nullptr); _ASSERT(FOH_COMMIT_SIZE >= MIN_OBJECT_SIZE); - _ASSERT(FOH_SEGMENT_SIZE > FOH_COMMIT_SIZE); - _ASSERT(FOH_SEGMENT_SIZE % FOH_COMMIT_SIZE == 0); // NOTE: objectSize is expected be the full size including header _ASSERT(objectSize >= MIN_OBJECT_SIZE); @@ -52,7 +50,7 @@ Object* FrozenObjectHeapManager::TryAllocateObject(PTR_MethodTable type, size_t if (m_CurrentSegment == nullptr) { // Create the first segment on first allocation - m_CurrentSegment = new FrozenObjectSegment(); + m_CurrentSegment = new FrozenObjectSegment(FOH_SEGMENT_DEFAULT_SIZE); m_FrozenSegments.Append(m_CurrentSegment); _ASSERT(m_CurrentSegment != nullptr); } @@ -63,7 +61,8 @@ Object* FrozenObjectHeapManager::TryAllocateObject(PTR_MethodTable type, size_t // to create a new one if (obj == nullptr) { - m_CurrentSegment = new FrozenObjectSegment(); + // Double the reserved size to reduce the number of frozen segments in apps with lots of frozen objects + m_CurrentSegment = new FrozenObjectSegment(m_CurrentSegment->GetSize() * 2); m_FrozenSegments.Append(m_CurrentSegment); // Try again @@ -77,17 +76,33 @@ Object* FrozenObjectHeapManager::TryAllocateObject(PTR_MethodTable type, size_t } -FrozenObjectSegment::FrozenObjectSegment(): +FrozenObjectSegment::FrozenObjectSegment(size_t size) : m_pStart(nullptr), m_pCurrent(nullptr), m_SizeCommitted(0), + m_Size(size), m_SegmentHandle(nullptr) COMMA_INDEBUG(m_ObjectsCount(0)) { - void* alloc = ClrVirtualAlloc(nullptr, FOH_SEGMENT_SIZE, MEM_RESERVE, PAGE_READWRITE); + _ASSERT(m_Size > FOH_COMMIT_SIZE); + _ASSERT(m_Size % FOH_COMMIT_SIZE == 0); + + void* alloc = ClrVirtualAlloc(nullptr, m_Size, MEM_RESERVE, PAGE_READWRITE); if (alloc == nullptr) { - ThrowOutOfMemory(); + // Try again with the default FOH size + if (m_Size > FOH_SEGMENT_DEFAULT_SIZE) + { + m_Size = FOH_SEGMENT_DEFAULT_SIZE; + _ASSERT(m_Size > FOH_COMMIT_SIZE); + _ASSERT(m_Size % FOH_COMMIT_SIZE == 0); + alloc = ClrVirtualAlloc(nullptr, m_Size, MEM_RESERVE, PAGE_READWRITE); + } + + if (alloc == nullptr) + { + ThrowOutOfMemory(); + } } // Commit a chunk in advance @@ -107,7 +122,7 @@ FrozenObjectSegment::FrozenObjectSegment(): si.ibFirstObject = sizeof(ObjHeader); si.ibAllocated = si.ibFirstObject; si.ibCommit = FOH_COMMIT_SIZE; - si.ibReserved = FOH_SEGMENT_SIZE; + si.ibReserved = m_Size; m_SegmentHandle = GCHeapUtilities::GetGCHeap()->RegisterFrozenSegment(&si); if (m_SegmentHandle == nullptr) @@ -125,13 +140,13 @@ FrozenObjectSegment::FrozenObjectSegment(): Object* FrozenObjectSegment::TryAllocateObject(PTR_MethodTable type, size_t objectSize) { - _ASSERT(m_pStart != nullptr && FOH_SEGMENT_SIZE > 0 && m_SegmentHandle != nullptr); // Expected to be inited + _ASSERT(m_pStart != nullptr && m_Size > 0 && m_SegmentHandle != nullptr); // Expected to be inited _ASSERT(IS_ALIGNED(m_pCurrent, DATA_ALIGNMENT)); _ASSERT(objectSize <= FOH_COMMIT_SIZE); _ASSERT(m_pCurrent >= m_pStart + sizeof(ObjHeader)); const size_t spaceUsed = (size_t)(m_pCurrent - m_pStart); - const size_t spaceLeft = FOH_SEGMENT_SIZE - spaceUsed; + const size_t spaceLeft = m_Size - spaceUsed; _ASSERT(spaceUsed >= sizeof(ObjHeader)); _ASSERT(spaceLeft >= sizeof(ObjHeader)); @@ -146,7 +161,7 @@ Object* FrozenObjectSegment::TryAllocateObject(PTR_MethodTable type, size_t obje if (spaceUsed + objectSize + sizeof(ObjHeader) > m_SizeCommitted) { // Make sure we don't go out of bounds during this commit - _ASSERT(m_SizeCommitted + FOH_COMMIT_SIZE <= FOH_SEGMENT_SIZE); + _ASSERT(m_SizeCommitted + FOH_COMMIT_SIZE <= m_Size); if (ClrVirtualAlloc(m_pStart + m_SizeCommitted, FOH_COMMIT_SIZE, MEM_COMMIT, PAGE_READWRITE) == nullptr) { diff --git a/src/coreclr/vm/frozenobjectheap.h b/src/coreclr/vm/frozenobjectheap.h index 44b00024196ef..570a7d432db4b 100644 --- a/src/coreclr/vm/frozenobjectheap.h +++ b/src/coreclr/vm/frozenobjectheap.h @@ -38,9 +38,14 @@ class FrozenObjectHeapManager class FrozenObjectSegment { public: - FrozenObjectSegment(); + FrozenObjectSegment(size_t size); Object* TryAllocateObject(PTR_MethodTable type, size_t objectSize); + size_t GetSize() const + { + return m_Size; + } + private: // Start of the reserved memory, the first object starts at "m_pStart + sizeof(ObjHeader)" (its pMT) uint8_t* m_pStart; @@ -56,6 +61,9 @@ class FrozenObjectSegment // m_SizeCommitted <= m_pStart + FOH_SIZE_RESERVED size_t m_SizeCommitted; + // Total memory reserved for the current segment + size_t m_Size; + segment_handle m_SegmentHandle; INDEBUG(size_t m_ObjectsCount); }; From f7e98c94f8e01b77bcdd0fd990be13a558c383f8 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Tue, 11 Oct 2022 18:00:47 +0200 Subject: [PATCH 2/2] Address feedback --- src/coreclr/vm/frozenobjectheap.cpp | 11 +++++++---- src/coreclr/vm/frozenobjectheap.h | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/coreclr/vm/frozenobjectheap.cpp b/src/coreclr/vm/frozenobjectheap.cpp index 66dbf4803a281..a1ed1c8b46dc4 100644 --- a/src/coreclr/vm/frozenobjectheap.cpp +++ b/src/coreclr/vm/frozenobjectheap.cpp @@ -62,7 +62,9 @@ Object* FrozenObjectHeapManager::TryAllocateObject(PTR_MethodTable type, size_t if (obj == nullptr) { // Double the reserved size to reduce the number of frozen segments in apps with lots of frozen objects - m_CurrentSegment = new FrozenObjectSegment(m_CurrentSegment->GetSize() * 2); + // Use the same size in case if prevSegmentSize*2 operation overflows. + size_t prevSegmentSize = m_CurrentSegment->GetSize(); + m_CurrentSegment = new FrozenObjectSegment(max(prevSegmentSize, prevSegmentSize * 2)); m_FrozenSegments.Append(m_CurrentSegment); // Try again @@ -75,12 +77,13 @@ Object* FrozenObjectHeapManager::TryAllocateObject(PTR_MethodTable type, size_t #endif // !FEATURE_BASICFREEZE } - -FrozenObjectSegment::FrozenObjectSegment(size_t size) : +// Reserve sizeHint bytes of memory for the given frozen segment. +// The requested size can be be ignored in case of memory pressure and FOH_SEGMENT_DEFAULT_SIZE is used instead. +FrozenObjectSegment::FrozenObjectSegment(size_t sizeHint) : m_pStart(nullptr), m_pCurrent(nullptr), m_SizeCommitted(0), - m_Size(size), + m_Size(sizeHint), m_SegmentHandle(nullptr) COMMA_INDEBUG(m_ObjectsCount(0)) { diff --git a/src/coreclr/vm/frozenobjectheap.h b/src/coreclr/vm/frozenobjectheap.h index 570a7d432db4b..35fc7c00332f6 100644 --- a/src/coreclr/vm/frozenobjectheap.h +++ b/src/coreclr/vm/frozenobjectheap.h @@ -38,7 +38,7 @@ class FrozenObjectHeapManager class FrozenObjectSegment { public: - FrozenObjectSegment(size_t size); + FrozenObjectSegment(size_t sizeHint); Object* TryAllocateObject(PTR_MethodTable type, size_t objectSize); size_t GetSize() const