diff options
Diffstat (limited to 'src/runtime')
| -rw-r--r-- | src/runtime/mgcmark_greenteagc.go | 20 |
1 files changed, 20 insertions, 0 deletions
diff --git a/src/runtime/mgcmark_greenteagc.go b/src/runtime/mgcmark_greenteagc.go index 75c347b9e9..ac2b1732f9 100644 --- a/src/runtime/mgcmark_greenteagc.go +++ b/src/runtime/mgcmark_greenteagc.go @@ -111,6 +111,26 @@ func (o *spanScanOwnership) or(v spanScanOwnership) spanScanOwnership { } func (imb *spanInlineMarkBits) init(class spanClass) { + if imb == nil { + // This nil check and throw is almost pointless. Normally we would + // expect imb to never be nil. However, this is called on potentially + // freshly-allocated virtual memory. As of 2025, the compiler-inserted + // nil check is not a branch but a memory read that we expect to fault + // if the pointer really is nil. + // + // However, this causes a read of the page, and operating systems may + // take it as a hint to back the accessed memory with a read-only zero + // page. However, we immediately write to this memory, which can then + // force operating systems to have to update the page table and flush + // the TLB, causing a lot of churn for programs that are short-lived + // and monotonically grow in size. + // + // This nil check is thus an explicit branch instead of what the compiler + // would insert circa 2025, which is a memory read instruction. + // + // See go.dev/issue/74375 for details. + throw("runtime: span inline mark bits nil?") + } *imb = spanInlineMarkBits{} imb.class = class } |
