-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Allocate string literals on frozen segments #49576
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
166ccfe
1cb0927
d2b9cbf
641365a
33a126b
bb1b835
2ef204a
a84a6ee
142295a
a446dd6
41665c3
66d6106
67f32a7
9ecf7d7
45a454a
1d0a710
201565e
c7dad85
4202d9f
69ab9d2
a144453
2571c9c
286f8ab
28e0331
6625759
89df356
3cb98e9
c6df9ed
f50b4b9
2b55835
6ea2a50
15ca880
193676e
9198043
7c98ef2
af6e445
2183c2f
4ef34dd
722eb82
0ae890a
e59d27a
90feb96
ce3e19e
b6fae80
32cb338
d20cc47
31b5fdc
1b1ab4c
ed8ed7b
88ad320
943a235
a3bfc8c
1be12c5
5c2a222
19d1734
4762bcc
2e03cc2
2f00f71
ddfbf73
598b9c8
802106e
bfe96e7
2e10c9c
3415dc3
bac0389
01b5731
8d139c9
b6deb3c
1ba9d97
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,135 +4,151 @@ | |
| #include "frozenobjectheap.h" | ||
| #include "memorypool.h" | ||
EgorBo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| FrozenObjectHeap::FrozenObjectHeap(): | ||
| m_pStart(nullptr), | ||
| m_pCurrent(nullptr), | ||
| m_CommitChunkSize(0), | ||
| m_SizeCommitted(0), | ||
| m_SizeReserved(0), | ||
| m_SegmentHandle(nullptr) | ||
| COMMA_INDEBUG(m_ObjectsCount(0)) | ||
|
|
||
| FrozenObjectHeapManager::FrozenObjectHeapManager(): | ||
| m_CurrentHeap(nullptr) | ||
| { | ||
| m_Crst.Init(CrstFrozenObjectHeap, CRST_UNSAFE_COOPGC); | ||
| m_HeapCommitChunkSize = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_FrozenSegmentCommitSize); | ||
| m_HeapSize = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_FrozenSegmentReserveSize); | ||
| } | ||
|
|
||
| FrozenObjectHeap::~FrozenObjectHeap() | ||
| // Allocates an object of the give size (including header) on a frozen segment. | ||
| // May return nullptr in the following cases: | ||
| // 1) DOTNET_FrozenSegmentReserveSize is 0 (disabled) | ||
| // 2) Object is too large (large than DOTNET_FrozenSegmentCommitSize) | ||
| // in such cases caller is responsible to find a more appropriate heap to allocate it | ||
| Object* FrozenObjectHeapManager::AllocateObject(size_t objectSize) | ||
| { | ||
| if (m_SegmentHandle != nullptr) | ||
| CONTRACTL | ||
| { | ||
| GCHeapUtilities::GetGCHeap()->UnregisterFrozenSegment(m_SegmentHandle); | ||
| THROWS; | ||
| MODE_COOPERATIVE; | ||
| } | ||
| CONTRACTL_END | ||
|
|
||
| CrstHolder ch(&m_Crst); | ||
|
|
||
| if (m_pStart != nullptr) | ||
| // Quick way to disable Frozen Heaps | ||
| if (m_HeapSize == 0) | ||
| { | ||
| ClrVirtualFree(m_pStart, 0, MEM_RELEASE); | ||
| return nullptr; | ||
| } | ||
| } | ||
|
|
||
| bool FrozenObjectHeap::Initialize() | ||
| { | ||
| // For internal testing | ||
| m_CommitChunkSize = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_FrozenSegmentCommitSize); | ||
| m_SizeReserved = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_FrozenSegmentReserveSize); | ||
| _ASSERT(m_HeapCommitChunkSize >= MIN_OBJECT_SIZE); | ||
| _ASSERT(m_HeapSize > m_HeapCommitChunkSize); | ||
| _ASSERT(m_HeapSize % m_HeapCommitChunkSize == 0); | ||
|
|
||
| if (m_SizeReserved == 0) | ||
| // NOTE: objectSize is expected be the full size including header | ||
| _ASSERT(objectSize >= MIN_OBJECT_SIZE); | ||
|
|
||
| if (objectSize > m_HeapCommitChunkSize) | ||
| { | ||
| // A way to disable FrozenObjectHeap | ||
| return false; | ||
| // The current design doesn't allow object larger than DOTNET_FrozenSegmentCommitSize and | ||
| // since FrozenObjectHeap is just an optimization, let's not fill it with huge objects. | ||
| return nullptr; | ||
| } | ||
|
|
||
| _ASSERT(m_SizeReserved > m_CommitChunkSize); | ||
| _ASSERT(m_SizeReserved % m_CommitChunkSize == 0); | ||
| _ASSERT(m_SegmentHandle == nullptr); | ||
| _ASSERT(m_pStart == nullptr); | ||
|
|
||
| void* alloc = ClrVirtualAlloc(nullptr, m_SizeReserved, MEM_RESERVE, PAGE_READWRITE); | ||
| if (alloc != nullptr) | ||
| if (m_CurrentHeap == nullptr) | ||
| { | ||
| // Commit FOH_COMMIT_SIZE chunk in advance | ||
| alloc = ClrVirtualAlloc(alloc, m_CommitChunkSize, MEM_COMMIT, PAGE_READWRITE); | ||
| // Create the first heap on first allocation | ||
| m_CurrentHeap = new FrozenObjectHeap(m_HeapSize, m_HeapCommitChunkSize); | ||
| m_FrozenHeaps.Append(m_CurrentHeap); | ||
| _ASSERT(m_CurrentHeap != nullptr); | ||
| } | ||
|
|
||
| if (alloc != nullptr) | ||
| Object* obj = m_CurrentHeap->AllocateObject(objectSize); | ||
|
|
||
| // The only case where it might be null is when the current heap is full and we need | ||
| // to create a new one | ||
| if (obj == nullptr) | ||
| { | ||
| // ClrVirtualAlloc is expected to be PageSize-aligned so we can expect | ||
| // DATA_ALIGNMENT alignment as well | ||
| _ASSERT(IS_ALIGNED(alloc, DATA_ALIGNMENT)); | ||
|
|
||
| segment_info si; | ||
| si.pvMem = alloc; | ||
| si.ibFirstObject = sizeof(ObjHeader); | ||
| si.ibAllocated = si.ibFirstObject; | ||
| si.ibCommit = m_CommitChunkSize; | ||
| si.ibReserved = m_SizeReserved; | ||
|
|
||
| m_SegmentHandle = GCHeapUtilities::GetGCHeap()->RegisterFrozenSegment(&si); | ||
| if (m_SegmentHandle != nullptr) | ||
| { | ||
| m_pStart = static_cast<uint8_t*>(alloc); | ||
| m_pCurrent = m_pStart; | ||
| m_SizeCommitted = si.ibCommit; | ||
| INDEBUG(m_ObjectsCount = 0); | ||
| return true; | ||
| } | ||
| m_CurrentHeap = new FrozenObjectHeap(m_HeapSize, m_HeapCommitChunkSize); | ||
| m_FrozenHeaps.Append(m_CurrentHeap); | ||
|
|
||
| // Try again | ||
| obj = m_CurrentHeap->AllocateObject(objectSize); | ||
|
|
||
| // GC refused to register frozen segment (OOM?) | ||
| ClrVirtualFree(m_pStart, 0, MEM_RELEASE); | ||
| m_pStart = nullptr; | ||
| // This time it's not expected to be null | ||
| _ASSERT(obj != nullptr); | ||
| } | ||
| return false; | ||
| return obj; | ||
| } | ||
|
|
||
|
|
||
| Object* FrozenObjectHeap::AllocateObject(size_t objectSize) | ||
| FrozenObjectHeap::FrozenObjectHeap(size_t reserveSize, size_t commitChunkSize): | ||
| m_pStart(nullptr), | ||
| m_pCurrent(nullptr), | ||
| m_CommitChunkSize(0), | ||
| m_SizeCommitted(0), | ||
| m_SegmentHandle(nullptr) | ||
| COMMA_INDEBUG(m_ObjectsCount(0)) | ||
| { | ||
| // NOTE: objectSize is expected be the full size including header | ||
| _ASSERT(objectSize >= MIN_OBJECT_SIZE); | ||
| m_SizeReserved = reserveSize; | ||
| m_CommitChunkSize = commitChunkSize; | ||
|
|
||
| CrstHolder ch(&m_Crst); | ||
| void* alloc = ClrVirtualAlloc(nullptr, m_SizeReserved, MEM_RESERVE, PAGE_READWRITE); | ||
| if (alloc == nullptr) | ||
| { | ||
| ThrowOutOfMemory(); | ||
| } | ||
|
|
||
| if (m_pStart == nullptr) | ||
| // Commit a chunk in advance | ||
| alloc = ClrVirtualAlloc(alloc, m_CommitChunkSize, MEM_COMMIT, PAGE_READWRITE); | ||
| if (alloc == nullptr) | ||
| { | ||
| // m_SizeReserved > 0 means we already tried to init and it failed. | ||
| // so bail out to avoid doing Alloc again. | ||
| if ((m_SizeReserved > 0) || !Initialize()) | ||
| { | ||
| return nullptr; | ||
| } | ||
| ThrowOutOfMemory(); | ||
EgorBo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| if (objectSize > m_CommitChunkSize) | ||
| // ClrVirtualAlloc is expected to be PageSize-aligned so we can expect | ||
| // DATA_ALIGNMENT alignment as well | ||
| _ASSERT(IS_ALIGNED(alloc, DATA_ALIGNMENT)); | ||
|
|
||
| segment_info si; | ||
| si.pvMem = alloc; | ||
| si.ibFirstObject = sizeof(ObjHeader); | ||
| si.ibAllocated = si.ibFirstObject; | ||
| si.ibCommit = m_CommitChunkSize; | ||
| si.ibReserved = m_SizeReserved; | ||
|
|
||
| m_SegmentHandle = GCHeapUtilities::GetGCHeap()->RegisterFrozenSegment(&si); | ||
| if (m_SegmentHandle == nullptr) | ||
| { | ||
| // The current design doesn't allow object larger than FOH_COMMIT_CHUNK_SIZE and | ||
| // since FrozenObjectHeap is just an optimization, let's not fill it with huge objects. | ||
| return nullptr; | ||
| ThrowOutOfMemory(); | ||
EgorBo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| _ASSERT(m_pStart != nullptr); | ||
| _ASSERT(m_SegmentHandle != nullptr); | ||
| m_pStart = static_cast<uint8_t*>(alloc); | ||
| m_pCurrent = m_pStart; | ||
| m_SizeCommitted = si.ibCommit; | ||
| INDEBUG(m_ObjectsCount = 0); | ||
| return; | ||
| } | ||
|
|
||
| Object* FrozenObjectHeap::AllocateObject(size_t objectSize) | ||
| { | ||
| _ASSERT(m_pStart != nullptr && m_SizeReserved > 0 && m_SegmentHandle != nullptr); // Expected to be inited | ||
| _ASSERT(IS_ALIGNED(m_pCurrent, DATA_ALIGNMENT)); | ||
|
|
||
| uint8_t* obj = m_pCurrent; | ||
| if ((size_t)(m_pStart + m_SizeReserved) < (size_t)(obj + objectSize)) | ||
| if (reinterpret_cast<size_t>(m_pStart + m_SizeReserved) < reinterpret_cast<size_t>(obj + objectSize)) | ||
| { | ||
| // heap is full, caller is expected to switch to other heaps | ||
| // TODO: register a new frozen segment | ||
| // heap is full | ||
|
||
| return nullptr; | ||
| } | ||
|
|
||
| // Check if we need to commit a new chunk | ||
| if ((size_t)(m_pStart + m_SizeCommitted) < (size_t)(obj + objectSize)) | ||
| if (reinterpret_cast<size_t>(m_pStart + m_SizeCommitted) < reinterpret_cast<size_t>(obj + objectSize)) | ||
| { | ||
| _ASSERT(m_SizeCommitted + m_CommitChunkSize <= m_SizeReserved); | ||
| if (ClrVirtualAlloc(m_pStart + m_SizeCommitted, m_CommitChunkSize, MEM_COMMIT, PAGE_READWRITE) == nullptr) | ||
| { | ||
| // We failed to commit a new chunk of the reserved memory | ||
| return nullptr; | ||
| ThrowOutOfMemory(); | ||
| } | ||
| m_SizeCommitted += m_CommitChunkSize; | ||
| } | ||
|
|
||
| INDEBUG(m_ObjectsCount++); | ||
|
|
||
| m_pCurrent = obj + objectSize; | ||
|
|
||
| // Notify GC that we bumped the pointer and, probably, committed more memory in the reserved part | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.