Go 1.26 Release Notes
Go 1.26, released on 10 February 2026, is the major release that follows Go 1.25 by the standard six-month cycle (release history). Its headline runtime changes make the Green Tea garbage collector the default (cutting GC overhead 10–40% in real programs), reduce baseline cgo call overhead by ~30%, randomize the heap base address on 64-bit platforms for security, and add an experimental goroutine-leak profile that reuses garbage-collector reachability to detect permanently-blocked goroutines. The language gains two small but real additions —
newaccepting an arbitrary expression, and self-referential generic type parameters — and thego fixcommand is rebuilt as the home of Go’s “modernizers.” Every claim here is taken from the official Go 1.26 release notes, which remain the authoritative source.
Mental Model
Treat a Go release note as four concentric rings, ordered by how often the change touches you:
flowchart TD L["Language<br/>new(expr), self-referential generics"] --> T T["Toolchain<br/>go fix modernizers, go mod init lower version, pprof flame-graph default"] --> R R["Runtime / GC / Compiler<br/>Green Tea default, faster cgo, heap randomization, goroutine-leak profile"] --> S S["Standard library + ports<br/>crypto/hpke, simd/archsimd, errors.AsType, port removals"] style L fill:#1e6f3a,color:#fff style T fill:#235a8c,color:#fff style R fill:#7a3b8c,color:#fff style S fill:#8c5a23,color:#fff
Diagram: the four rings of the Go 1.26 release. The insight is that the runtime ring is where 1.26 is most consequential — Green Tea and the cgo speedup change the performance of every program automatically, with no source edits — while the language ring is deliberately tiny, reflecting the Go 1 promise’s bias against language churn.
Mechanical Walk-through
Language changes
Go 1.26 makes exactly two language changes — a reminder of how conservatively the language evolves under the Go 1 compatibility promise.
The built-in new function now accepts an expression, not just a type (Go 1.26 release notes). Before 1.26, new(T) allocated a zero T and returned *T; you had to allocate, then assign. Now new(expr) allocates storage, initializes it to the value of expr, and returns a pointer to it. The motivating use case is optional struct fields: building a *int for a JSON field previously required a helper or a temporary variable; new(yearsSince(born)) does it inline.
The second change lifts the restriction against a generic type referring to itself in its own type-parameter list (Go 1.26 release notes). A constraint like type Adder[A Adder[A]] interface { Add(A) A } — the curiously-recurring-template pattern, where a type is constrained to “be addable to its own kind” — is now legal. This unblocks a family of generic APIs that were previously inexpressible.
Runtime: the Green Tea garbage collector becomes the default
This is the most significant change. Green Tea — experimental in Go 1.25 — is now the default garbage collector in Go 1.26 (Go 1.26 release notes; Green Tea blog). It is a redesign of the mark/scan phase that improves locality and CPU scalability when marking and scanning small objects, and on newer amd64 microarchitectures (Intel Ice Lake, AMD Zen 4 and later) it uses vector instructions for an additional gain.
The reported impact is a 10–40% reduction in garbage-collection overhead in real-world programs, with a further ~10% on the newer amd64 platforms (Go 1.26 release notes). The opt-out is a build-time GOEXPERIMENT=nogreenteagc, and the release notes state that opt-out is expected to be removed in Go 1.27 — so 1.26 is the last release where you can easily revert. The deep mechanics live in Green Tea Garbage Collector.
Runtime: faster cgo calls
Go 1.26 reduces the baseline runtime overhead of a cgo call by roughly 30% (Go 1.26 release notes). A cgo call must transition the calling goroutine off the Go scheduler’s view (so the scheduler knows the thread is in C and can run other goroutines), and that bookkeeping is the “baseline overhead” being trimmed. The change is transparent: any program calling C through cgo gets the speedup with no edits. The cost model and the remaining overhead are covered in cgo Performance and Pitfalls.
Runtime: heap base address randomization
On 64-bit platforms the runtime now randomizes the heap base address at process startup (Go 1.26 release notes). This is a security hardening: it makes the addresses of heap objects unpredictable, raising the bar for memory-corruption exploits — the threat is especially relevant when cgo lets attacker-influenced C code interact with Go memory. The opt-out is GOEXPERIMENT=norandomizedheapbase64, expected to be removed in a future release.
Runtime: the experimental goroutine-leak profile
Go 1.26 adds an experimental goroutine-leak profile (Go 1.26 release notes). It is enabled at build time with GOEXPERIMENT=goroutineleakprofile and then exposed as a goroutineleak profile type in runtime/pprof, and as the /debug/pprof/goroutineleak endpoint via net/http/pprof.
The clever part is the definition and the detection method. A leaked goroutine is one blocked on a concurrency primitive — a channel, sync.Mutex, sync.Cond, and so on — that cannot possibly become unblocked. The runtime detects this by reusing the garbage collector’s reachability analysis: if goroutine G is blocked on primitive P, and P is unreachable from any runnable goroutine (or any goroutine those could transitively unblock), then no code can ever signal P, so G is permanently stuck. The release notes credit the work to Vlad Saioc at Uber, describe the implementation as production-ready (experimental only for API feedback), note there is no runtime overhead unless the profile is in use, and state the aim to enable it by default in Go 1.27. The leak-detection idea connects Goroutine Leaks and the Go Garbage Collector.
Compiler
The compiler can now allocate the backing store of a slice on the stack in more situations (Go 1.26 release notes) — an extension of Escape Analysis that keeps more slices off the heap, reducing allocation pressure. If a program misbehaves, the bisect tool with -compile=variablemake localizes the offending allocation, and -gcflags=all=-d=variablemakehash=n disables the new behavior entirely.
Linker
The linker gains internal linking mode for cgo programs on windows/arm64, requested with -ldflags=-linkmode=internal. Several executable-format details changed — moduledata moved to its own .go.module section, the cutab field length was corrected, .gopclntab moved from the relro segment to rodata and lost its relocations, the always-empty .gosymtab section was removed. These do not affect running programs but matter to tools that parse Go binaries — see Go Executable Layout.
Bootstrap and ports
Go 1.26 requires Go 1.24.6 or later to bootstrap (Go 1.26 release notes). Port changes: it is the last release supporting macOS 12 Monterey (1.27 needs Ventura) and the last supporting the ELFv1 ABI on big-endian linux/ppc64; the broken 32-bit windows/arm port is removed; linux/riscv64 gains the race detector; freebsd/riscv64 is marked broken.
Configuration / Code examples
The new new
type Person struct {
Name string `json:"name"`
Age *int `json:"age"` // age if known; nil otherwise
}
func personJSON(name string, born time.Time) ([]byte, error) {
return json.Marshal(Person{
Name: name,
Age: new(yearsSince(born)), // 1.26: new(expr) — was new(int) + assign
})
}- Pre-1.26 this required
n := yearsSince(born); Age: &n. Thenew(expr)form allocates, stores the computed age, and yields the*intin one expression. Example adapted from the Go 1.26 release notes.
Opting out of Green Tea and the leak profiler
# Build with the previous (pre-Green-Tea) collector — last possible in 1.26:
$ GOEXPERIMENT=nogreenteagc go build ./...
# Enable the experimental goroutine-leak profile:
$ GOEXPERIMENT=goroutineleakprofile go build -o svc ./...
$ ./svc & # then, with net/http/pprof imported:
$ go tool pprof http://localhost:6060/debug/pprof/goroutineleakGOEXPERIMENTis a build-time variable consumed when compiling the runtime, not a runtime environment variable — contrast with theGODEBUGknobs in GODEBUG and Runtime Configuration Knobs.- The leak profile costs nothing unless something actually requests it.
A leak the profiler catches
func processWorkItems(ws []workItem) ([]workResult, error) {
ch := make(chan result) // unbuffered
for _, w := range ws {
go func() { ch <- result{processWorkItem(w)} }() // N senders
}
var results []workResult
for range len(ws) {
r := <-ch
if r.err != nil {
return nil, r.err // early return — remaining senders block on ch forever
}
results = append(results, r.res)
}
return results, nil
}- On the early
return, the receiver stops; the still-running sender goroutines block onch <- ...permanently. After the function returns,chis unreachable from any runnable goroutine, so the GC-based detector flags every blocked sender as leaked. Example from the Go 1.26 release notes; the underlying anti-pattern is Goroutine Leak via Blocked Channel.
Failure Modes / Common Misunderstandings
“Green Tea changes my program’s behavior.” It changes performance characteristics (overhead, locality), not observable semantics — the collector is still concurrent, non-moving, tricolor mark-and-sweep. If a workload regresses, the release notes explicitly ask you to file an issue rather than silently set nogreenteagc, because the opt-out is going away in 1.27.
“GOEXPERIMENT=goroutineleakprofile slows down production.” The release notes state there is no additional runtime overhead unless the profile is actively in use. The “experimental” label is about API stability, not production-readiness — the implementation is described as production-ready.
“go mod init gives me Go 1.26.” As of 1.26, go mod init deliberately writes a lower go line — go 1.25.0 for the 1.26 release, go 1.24.0 for a release candidate (Go 1.26 release notes). New modules do not immediately demand the newest language version.
“The crypto random-parameter change broke my tests.” Across crypto/rsa, crypto/ecdsa, crypto/ecdh, crypto/ed25519, crypto/dsa, and crypto/rand, the io.Reader random parameter to key-generation and signing functions is now ignored — the implementations always use a secure source. Tests that injected a deterministic reader must switch to testing/cryptotest.SetGlobalRandom; the GODEBUG=cryptocustomrand=1 setting restores the old behavior as a transition aid.
“My binary-analysis tool broke.” The 1.26 linker moved several sections (moduledata to .go.module, funcdata into .gopclntab) and removed .gosymtab. Running programs are unaffected, but tools that parse the Go-specific sections of an executable need updating.
Alternatives and When to Choose Them
A “release note” is not a thing you choose between — but the adoption decision it forces is. When Go 1.26 ships, you can:
- Upgrade promptly. Recommended for most: the Green Tea and cgo wins are free, and the support window means staying current is the only way to keep receiving security patches (Go Release Cycle and Versioning).
- Upgrade the toolchain but keep an older
goline. This takes the compiler/runtime improvements (Green Tea, faster cgo are toolchain-level) while keeping version-gated behavioral defaults pinned via thegodirective — useful if aGODEBUG-guarded change is risky for you. See GODEBUG and Runtime Configuration Knobs. - Delay. Only defensible briefly; two majors back you lose security support entirely.
Production Notes
For most services the 1.26 upgrade is a rebuild: Green Tea and the ~30% cgo speedup require no source changes and frequently show up as a measurable drop in GC CPU and tail latency — re-run pprof and Profiling and the The Go Execution Tracer before and after to quantify it. The crypto random-parameter changes are the most likely source-visible breakage and almost always surface in tests, not production.
The goroutine-leak profile is worth wiring into a staging build now even though it is experimental: leaked goroutines are a classic slow-burn production failure (steady memory growth, eventual OOM) that the existing goroutine profile only shows as a snapshot — the new profile distinguishes “currently blocked” from “permanently leaked,” which is the distinction that actually matters during an incident.
Heap base randomization is invisible unless something depended on stable heap addresses across runs — which nothing well-behaved should — so it is effectively a free security improvement.
See Also
- Go Release Cycle and Versioning — the cadence that produced this release
- Green Tea Garbage Collector — the marquee runtime change, in depth
- cgo Performance and Pitfalls — the ~30% overhead reduction in context
- Goroutine Leaks — what the new leak profile detects
- GODEBUG and Runtime Configuration Knobs — the
cryptocustomrandand other transition knobs - Escape Analysis — the slice-backing-store stack-allocation change
- Go Internals MOC — parent map