Skip to content

runtime: ensure some headroom for the GC to run #2884

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

Merged
merged 1 commit into from
Sep 16, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 25 additions & 3 deletions src/runtime/gc_conservative.go
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,14 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer {
// could be found. Run a garbage collection cycle to reclaim
// free memory and try again.
heapScanCount = 2
GC()
freeBytes := runGC()
heapSize := uintptr(metadataStart) - heapStart
if freeBytes < heapSize/3 {
// Ensure there is at least 33% headroom.
// This percentage was arbitrarily chosen, and may need to
// be tuned in the future.
growHeap()
}
} else {
// Even after garbage collection, no free memory could be found.
// Try to increase heap size.
Expand Down Expand Up @@ -379,6 +386,13 @@ func free(ptr unsafe.Pointer) {

// GC performs a garbage collection cycle.
func GC() {
runGC()
}

// runGC performs a garbage colleciton cycle. It is the internal implementation
// of the runtime.GC() function. The difference is that it returns the number of
// free bytes in the heap after the GC is finished.
func runGC() (freeBytes uintptr) {
if gcDebug {
println("running collection cycle...")
}
Expand Down Expand Up @@ -420,12 +434,14 @@ func GC() {

// Sweep phase: free all non-marked objects and unmark marked objects for
// the next collection cycle.
sweep()
freeBytes = sweep()

// Show how much has been sweeped, for debugging.
if gcDebug {
dumpHeap()
}

return
}

// markRoots reads all pointers from start to end (exclusive) and if they look
Expand Down Expand Up @@ -568,7 +584,8 @@ func markRoot(addr, root uintptr) {
}

// Sweep goes through all memory and frees unmarked memory.
func sweep() {
// It returns how many bytes are free in the heap after the sweep.
func sweep() (freeBytes uintptr) {
freeCurrentObject := false
for block := gcBlock(0); block < endBlock; block++ {
switch block.state() {
Expand All @@ -577,20 +594,25 @@ func sweep() {
block.markFree()
freeCurrentObject = true
gcFrees++
freeBytes += bytesPerBlock
case blockStateTail:
if freeCurrentObject {
// This is a tail object following an unmarked head.
// Free it now.
block.markFree()
freeBytes += bytesPerBlock
}
case blockStateMark:
// This is a marked object. The next tail blocks must not be freed,
// but the mark bit must be removed so the next GC cycle will
// collect this object if it is unreferenced then.
block.unmark()
freeCurrentObject = false
case blockStateFree:
freeBytes += bytesPerBlock
}
}
return
}

// looksLikePointer returns whether this could be a pointer. Currently, it
Expand Down