aboutsummaryrefslogtreecommitdiff
path: root/src/runtime
diff options
context:
space:
mode:
Diffstat (limited to 'src/runtime')
-rw-r--r--src/runtime/mgcmark_greenteagc.go20
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
}