TL;DR

Go 1.26 (February 10, 2026) makes the Green Tea garbage collector the default, which the Go team says cuts GC overhead by 10-40% with no code changes. It also lets new take an expression, allows generic types to refer to themselves, rewrites go fix, and adds three experiments behind GOEXPERIMENT flags: simd, runtimesecret and goroutineleakprofile. Each feature below comes with code you can run.

Go 1.27 followed on August 19, 2026. It made the leak profiler generally available and extended SIMD to arm64 and WebAssembly, but left SIMD and runtime/secret behind their flags. Go 1.26 is still supported (go1.26.8 is the current patch) until Go 1.28 ships, and the section near the end covers what changed and which version to pick.

Release status last verified: October 6, 2026.

Part of our Modern Go in 2026 series — the complete guide connects this release to iterators, generic methods, JSON v2, and where Go beats Rust.

Why 1.26 Is Worth the Upgrade

The biggest change needs no code at all. Services that churn small, short-lived objects (caches, request routers, JSON-heavy handlers) get the new garbage collector by bumping the Go version in the Dockerfile. The Go team quotes a 10-40% reduction in GC overhead for real-world programs that lean heavily on the collector. The rest of the release is smaller, though several pieces pay off on day one.

Go 1.26 shipped on February 10, 2026, six months after 1.25. The release notes run long: language changes, three experiments, a crypto overhaul, and dozens of standard library additions. This tutorial walks through ten of them. If you’re choosing between 1.26 and 1.27, jump to the 1.27 section. Most of what follows applies to both releases, since 1.27 extends these features.

1. Green Tea GC Becomes the Default

The Green Tea garbage collector first appeared as an opt-in experiment in Go 1.25. In Go 1.26, it’s the default. The previous object-by-object marking algorithm is still available behind GOEXPERIMENT=nogreenteagc. The 1.26 notes said the team expected to remove that escape hatch in Go 1.27, but that didn’t happen on schedule. The Go 1.27 release notes don’t mention the collector at all, and the go1.27.0 source still lists GreenTeaGC as an experiment that’s on by default (internal/buildcfg/exp.go), so the opt-out still parses. A later release can remove it, so don’t build a deployment plan around it.

What changed: Green Tea is still a concurrent mark-sweep collector, but it marks and scans whole memory pages at a time instead of chasing one object after another, which gives better CPU locality and scalability on small objects. On newer amd64 hardware (Intel Ice Lake, AMD Zen 4+), the Green Tea design post reports an additional ~10% from AVX-512 vector instructions. How much you gain overall depends on your allocation patterns, with small-object churn benefiting most.

Here’s a quick benchmark you can run to see the difference on your own hardware. Two details matter here. The allocations go into a package-level slice so they escape to the heap (a make that never escapes gets stack-allocated, and the GC never sees it). And it reports GC CPU time instead of pause time, because Green Tea’s savings come from cheaper marking work.

package main

import (
	"fmt"
	"runtime"
	"runtime/metrics"
	"time"
)

// sink is package-level, so every allocation escapes to the heap.
var sink [][]byte

func churn(n, live int) {
	sink = make([][]byte, live)
	for i := 0; i < n; i++ {
		// keep `live` objects reachable, drop the older ones
		sink[i%live] = make([]byte, 128)
	}
}

func main() {
	start := time.Now()
	churn(20_000_000, 100_000)
	elapsed := time.Since(start)

	gcCPU := []metrics.Sample{{Name: "/cpu/classes/gc/total:cpu-seconds"}}
	metrics.Read(gcCPU)

	var stats runtime.MemStats
	runtime.ReadMemStats(&stats)

	fmt.Printf("Wall time:   %v\n", elapsed)
	fmt.Printf("GC cycles:   %d\n", stats.NumGC)
	fmt.Printf("GC CPU time: %.3fs\n", gcCPU[0].Value.Float64())
}

Run it on Go 1.26 twice: once with the default (go run main.go), then with the old collector (GOEXPERIMENT=nogreenteagc go run main.go). Compare the GC CPU time and Wall time lines, and add GODEBUG=gctrace=1 if you want a per-cycle breakdown. The size of the difference depends on your CPU and core count, so run it on the hardware you actually deploy to. If you run GC-heavy services, upgrade and measure.

2. new(expr) Lets You Initialize On Allocation

Before 1.26, creating a pointer to a non-zero value required a temporary variable:

// Go 1.25 and earlier
x := int64(300)
ptr := &x

// Or for struct fields:
type Config struct {
	Timeout *time.Duration
}

d := 30 * time.Second
cfg := Config{Timeout: &d}

Go 1.26 lets new take an expression:

// Go 1.26
ptr := new(int64(300))

cfg := Config{
	Timeout: new(30 * time.Second),
}

This is particularly useful with serialization packages where optional fields use pointers to distinguish “zero value” from “not set.” It also works with function calls:

ptr := new(yearsSince(born))

Code written before 1.26 often has this temporary-variable dance, and the newexpr analyzer in go fix (section 6) rewrites the helper-function version of it for you.

3. Self-Referential Generic Types

Go 1.26 lifts a restriction that prevented generic types from referring to themselves in their own type parameter lists. Before 1.26, you couldn’t express “a type that can operate on values of its own kind” at the type level:

// This now compiles in Go 1.26
type Adder[A Adder[A]] interface {
	Add(A) A
}

type Vec2 struct {
	X, Y float64
}

func (v Vec2) Add(other Vec2) Vec2 {
	return Vec2{X: v.X + other.X, Y: v.Y + other.Y}
}

// Vec2 satisfies Adder[Vec2]
func Sum[T Adder[T]](items []T) T {
	var result T
	for _, item := range items {
		result = result.Add(item)
	}
	return result
}

This pattern shows up in numerical code, graph algorithms, and any domain where you need “a thing that can combine with other things of the same type.” The compiler would reject it in 1.25, but 1.26 handles it without issue. The generics rules keep loosening release over release: Go 1.27 adds generic methods, letting a method declare its own type parameters separate from the receiver’s.

4. Experimental SIMD: Vector Operations in Go

SIMD (Single Instruction, Multiple Data) support lands in Go 1.26 as an experimental package. Enable it at build time:

GOEXPERIMENT=simd go build ./...

In Go 1.26 the simd/archsimd package exposes 128-bit, 256-bit, and 512-bit vector types for amd64 only. Here’s a basic example that adds two vectors of 4 float32 values in a single instruction:

//go:build goexperiment.simd

package main

import (
	"fmt"
	"simd/archsimd"
)

func main() {
	a := archsimd.BroadcastFloat32x4(3.0)  // [3, 3, 3, 3]
	b := archsimd.BroadcastFloat32x4(7.0)  // [7, 7, 7, 7]
	c := a.Add(b)
	fmt.Println(c) // {10,10,10,10}
}

The API isn’t stable yet, so expect changes before it graduates from experimental. If you write performance-sensitive numeric code such as image processing or ML inference, keep an eye on it. The alternative today is dropping into assembly or using cgo to call C SIMD intrinsics, both of which hurt readability and add maintenance cost.

The vector types are opaque (Int8x16, Float32x4, Float64x8, and so on), each with constructor functions like BroadcastFloat32x4 and LoadFloat32x4 plus arithmetic methods.

Go 1.27 widened this in two directions. simd/archsimd now supports 128-bit vectors on arm64 (Neon) and WebAssembly as well as amd64, so the Graviton and Apple Silicon crowd can finally try it. And a second experimental package, plain simd, adds portable vector types of unspecified width (Int8s, Float32s) that work on every architecture. The portable package is the one most application code should target once it stabilizes. archsimd remains the choice for code that needs a specific instruction width, but the 1.27 notes say its amd64 API was revised, so 1.26-era archsimd code may need touch-ups after the upgrade. Both are still behind GOEXPERIMENT=simd.

5. runtime/secret: Secure Memory Erasure

Cryptographic code has a problem: after you’re done with a key or password, the bytes sit in memory until the GC reclaims them. Even then, the OS might not zero the page. Attackers with memory access (cold boot attacks, core dumps, swap file forensics) can recover secrets that the application thought it had discarded.

Go 1.26 adds runtime/secret to fix this. Enable it with GOEXPERIMENT=runtimesecret:

//go:build goexperiment.runtimesecret

package main

import (
	"crypto/aes"
	"crypto/rand"
	"fmt"
	"runtime/secret"
)

func main() {
	key := make([]byte, 32)
	rand.Read(key)

	// Registers and stack used inside f are erased before Do
	// returns; heap allocations made inside f (like the AES key
	// schedule) are erased once the GC reclaims them.
	secret.Do(func() {
		block, err := aes.NewCipher(key)
		if err != nil {
			panic(err)
		}
		fmt.Printf("Cipher block size: %d\n", block.BlockSize())
	})
	// key was allocated outside Do, so clearing it is still your job.
	clear(key)
}

Wrapping a whole function, instead of offering a Zero(buf) helper, is deliberate. You can already zero a slice you own (the built-in clear does that), but you can’t reach the copies Go makes on your behalf: values spilled into registers and stack frames, or internal buffers such as the expanded AES key schedule. secret.Do erases the registers and stack that f used before it returns, and heap allocations made inside f are erased when the garbage collector finds them unreachable. It supports amd64 and arm64 on Linux.

Go 1.27 closed one gap: goroutines started while inside secret.Do now run in secret mode themselves, so a worker spawned from the protected function gets the same erasure treatment as the function itself. The package is still experimental in 1.27. For anything that handles encryption keys, API tokens, or passwords, read the runtime/secret package docs and watch the release notes for its graduation.

6. The Rewritten go fix Command

The go fix command has been completely rewritten. The old version shipped a handful of historical fixers that hadn’t been relevant for years. The new go fix is built on the Go analysis framework (the same infrastructure as go vet) and ships with a couple dozen “modernizers”: analyzers that detect old patterns and rewrite them to use newer language and standard library features. Each one only fires when your module’s go directive is new enough for the replacement.

Preview the changes first, then apply them:

go fix -diff ./...   # print the proposed edits without touching files
go fix ./...         # apply them

The Go blog’s go fix walkthrough shows two typical rewrites. The minmax analyzer collapses clamping logic into the min and max builtins from Go 1.21:

// Before
x := f()
if x < 0 {
	x = 0
}
if x > 100 {
	x = 100
}

// After
x := min(max(f(), 0), 100)

And rangeint turns counting loops whose index is never used into the range-over-int form from Go 1.22:

// Before
for i := 0; i < n; i++ {
	f()
}

// After
for range n {
	f()
}

There’s also newexpr, which ties back to section 2: it finds helper functions like func newInt(x int) *int { return &x } and replaces their calls with new(x). Each analyzer matches a narrower pattern than its name suggests. slicessort, for example, only rewrites the basic sort.Slice(s, func(i, j int) bool { return s[i] < s[j] }) form into slices.Sort(s); comparator-based sorts stay as written. The modernize docs spell out the exact pattern each one targets.

The new go fix also introduces //go:fix inline directives that let library authors mark functions as candidates for source-level inlining during fix passes. That’s handy for deprecations: the author ships a mechanical rewrite to the replacement along with the deprecation notice.

Go 1.27 added four more modernizers (atomictypes, embedlit, slicesbackward, unsafefuncs), dropped fmtappendf over style objections, and renamed waitgroup to waitgroupgo. Run go fix ./... on a branch and review the diff like any other change. A codebase with code dating back to the 1.18 era will give it plenty to do.

7. errors.AsTypeT for Type-Safe Error Unwrapping

errors.As has always required a pointer-to-interface argument, which is verbose and slightly error-prone:

// Go 1.25 and earlier
var pathErr *os.PathError
if errors.As(err, &pathErr) {
	fmt.Println(pathErr.Path)
}

Go 1.26 adds a generic version:

// Go 1.26
if pathErr, ok := errors.AsType[*os.PathError](err); ok {
	fmt.Println(pathErr.Path)
}

errors.AsType[T]() returns (T, bool). No pointer gymnastics, and the type constraint is enforced at compile time. The release notes add that it’s faster than the reflection-based errors.As. You don’t have to hunt down the old call sites by hand either: the modernize suite in golang.org/x/tools now has an errorsastype analyzer that rewrites errors.As calls into this form.

8. io.ReadAll Got 2x Faster

io.ReadAll got a rewrite. The old implementation over-allocated intermediate buffers during growth. The new version allocates less intermediate memory and returns a minimally sized final slice.

The release notes report roughly 2x throughput and about half the total memory allocation. If you’re reading HTTP response bodies, loading config files, or slurping stdin, this is a free speedup on upgrade.

9. Reflect Iterators and Other Standard Library Wins

Go 1.26 adds iterator methods to the reflect package, aligning with the iterator pattern introduced in Go 1.23:

t := reflect.TypeOf(MyStruct{})
for field := range t.Fields() {
	fmt.Printf("%s (%s)\n", field.Name, field.Type)
}

Same pattern for Type.Methods(), Type.Ins(), Type.Outs(), Value.Fields(), and Value.Methods(). The Type methods return an iter.Seq; the Value methods return an iter.Seq2 that pairs each StructField or Method with its Value. If you’ve ever written for i := 0; i < t.NumField(); i++ loops, this is a direct replacement.

Other standard library changes from the release notes:

PackageChangePractical effect
log/slogNewMultiHandler — fan out to multiple handlersStructured logging to stdout + file + external service without a wrapper
netDialer.DialTCP, DialUDP, DialIP, DialUnixType-specific dial methods with context support
net/httpServeMux trailing slash redirects → 307 instead of 301POST requests no longer lose their body on redirect
testingT.ArtifactDir()Save test artifacts (screenshots, logs); go test -artifacts keeps them after the run
crypto/hpkeHybrid Public Key Encryption (RFC 9180)Post-quantum key encapsulation built into the standard library
image/jpegEncoder and decoder rewrittenFaster, more accurate — but bit-for-bit output may differ

10. Goroutine Leak Profiler

Goroutine leaks are a common class of production bug in Go. A goroutine blocked on an unreachable channel or mutex never gets collected, and your process slowly bleeds memory. Go 1.26 adds an experimental profiler that catches them:

GOEXPERIMENT=goroutineleakprofile go build ./...

Once enabled, a new /debug/pprof/goroutineleak endpoint appears in net/http/pprof. It reports goroutines blocked on concurrency primitives (channels, sync.Mutex, sync.Cond) where the other side of the primitive is unreachable. Nobody will ever send on that channel or release that lock.

The profiler has zero overhead when not actively collecting a profile. It’s also the only 1.26 experiment that has graduated so far: in Go 1.27 the goroutineleak profile type is generally available in runtime/pprof, the /debug/pprof/goroutineleak endpoint works without any flag, and the goroutineleakprofile experiment setting has been deleted. On 1.27, drop that GOEXPERIMENT value from your build scripts: the go command rejects unknown experiment names, so a leftover goroutineleakprofile fails the build with an “unknown GOEXPERIMENT” error.

For server applications, wire it into your existing pprof endpoint and check it during load tests, since leaks that only show up under load are the hardest ones to find any other way.

Go 1.26 vs Go 1.27: Where the Experiments Ended Up

Go 1.27 landed on August 19, 2026, right on the six-month cadence. Of the three experiments covered above, only the leak profiler graduated. Most of the release is new material: the v2 JSON engine becomes the default, methods get their own type parameters, and a uuid package joins the standard library. Here is how the pieces line up across the two releases, read from the 1.27 release notes:

FeatureGo 1.26Go 1.27
Green Tea GCDefault, opt out with nogreenteagcStill default; notes are silent, and the opt-out flag is still in the source
simd/archsimdExperimental, amd64 onlyStill experimental; revised amd64 API, 128-bit vectors on arm64 and wasm, plus a portable simd package
runtime/secretExperimental (runtimesecret)Still experimental; goroutines spawned in secret mode inherit it
Goroutine leak profileExperimental (goroutineleakprofile)Generally available; the experiment setting is deleted
encoding/json v2Experimental (jsonv2)encoding/json now runs on the v2 implementation; encoding/json/v2 ships as a package; nojsonv2 opts out
Generic methodsNot allowedSupported (methods can declare their own type parameters)
Minimum macOSmacOS 12 Monterey (last release to support it)macOS 13 Ventura

The JSON row is the one to plan around. Upgrading to 1.27 swaps the engine underneath every existing json.Marshal and json.Unmarshal call. The release notes promise that marshaling and unmarshaling behavior is preserved and only the exact text of error messages may differ, with unmarshaling “significantly faster.” The catch is any test that asserts on JSON error strings. GOEXPERIMENT=nojsonv2 restores the old implementation while you fix those, and the notes already flag that opt-out for removal in a future release. The stricter defaults (rejecting invalid UTF-8 and duplicate keys) only apply if you move to the new encoding/json/v2 API, which our JSON v2 guide covers.

Picking a version comes down to support windows and build machines. Go supports each major release until two newer ones exist, which keeps 1.26 on security fixes until Go 1.28 ships, expected around February 2027. My take is to start new services on 1.27, since there’s no reason to adopt a release that’s already one behind. Teams already on 1.26 can move after a clean CI run, with nojsonv2 as a bridge if error-string assertions fail. Before that run, grep go.mod and your //go:debug lines for the GODEBUG settings 1.27 removed for good (asynctimerchan, tlsrsakex, tls10server, tls3des, tlsunsafeekm, x509keypairleaf, gotypesalias): if any is pinned to its old value, the go command refuses to build. If you’re on 1.25 or older, jump straight to 1.27: almost everything in this article carries over (the leak profiler’s build flag is the exception), and you pick up the GC win along the way. The one real reason to stay put is a fleet of macOS 12 build machines, because 1.27 won’t run there.

Upgrading

# Latest 1.26 patch release
go install golang.org/dl/go1.26.8@latest
go1.26.8 download

# Or the current 1.27 release
go install golang.org/dl/go1.27.1@latest
go1.27.1 download

# Or update your Dockerfile
FROM golang:1.26.8-alpine AS builder

After upgrading, run go fix ./..., then your existing tests and benchmarks. If something regresses and you suspect the collector, rebuild with GOEXPERIMENT=nogreenteagc to confirm before filing an issue upstream.

A few things to watch:

  • macOS 12 Monterey: Go 1.26 is the last version supporting it. Go 1.27 requires macOS 13 Ventura or later, so Monterey build machines are stuck on 1.26.x.
  • 32-bit Windows ARM: the windows/arm port was removed entirely.
  • net/url.Parse now rejects URLs with unbracketed colons in the host (e.g., http://::1/). Use http://[::1]/ for IPv6 literal addresses. Set GODEBUG=urlstrictcolons=0 to restore the old behavior temporarily.

FAQ

What is the Green Tea GC in Go 1.26?

Green Tea is Go’s new garbage collector, enabled by default starting in Go 1.26. It keeps Go’s concurrent mark-sweep model but marks and scans memory a page at a time instead of object by object, which gives it better CPU locality on small objects. The Go team reports 10-40% less GC overhead in real-world programs that lean heavily on the garbage collector, with additional gains on newer amd64 CPUs that support AVX-512.

When was Go 1.26 released?

Go 1.26 was released on February 10, 2026, following the standard six-month release cycle after Go 1.25. The latest patch release in the 1.26 line is go1.26.8, released September 1, 2026.

What is the latest version of Go?

Go 1.27 is the latest major release, out since August 19, 2026, and go1.27.1 (September 1, 2026) is the current patch. Go 1.26 is still supported and receives security fixes until Go 1.28 is released, expected around February 2027. The release history page lists every patch with its date.

How do I enable SIMD in Go 1.26?

Set GOEXPERIMENT=simd at build time: GOEXPERIMENT=simd go build ./.... In Go 1.26 the simd/archsimd package is amd64-only and the API is not yet stable. Go 1.27 keeps the same flag, adds arm64 and WebAssembly support, and introduces a portable simd package.

Does Go 1.26 break backward compatibility?

Go 1.26 follows the Go 1 compatibility promise. Existing code should compile and run without changes. The main breaking changes are behavioral: net/url.Parse rejects bare colons in hostnames, image/jpeg output may differ bit-for-bit, and crypto/dsa now ignores user-supplied randomness in favor of secure defaults. The URL and crypto changes have GODEBUG settings that restore the old behavior (urlstrictcolons=0, cryptocustomrand=1); the JPEG change has none.

Is Go 1.26 faster than Go 1.25?

For most programs, yes. Per the release notes, Green Tea GC reduces GC overhead by 10-40%, the baseline overhead of cgo calls drops by about 30%, io.ReadAll is roughly 2x faster, and the compiler allocates more slice backing stores on the stack. The exact gains depend on your workload and allocation patterns.

For the broader arc these releases fit into, see the modern Go guide.

Sources

Bottom Line

Go 1.26 was a “just upgrade” release, and eight months later it still reads that way. Go 1.27 kept its collector and its go fix, left SIMD and runtime/secret as experiments, and promoted the leak profiler. The extra chores 1.27 brings are small and mechanical: check JSON error-string tests, remove pinned GODEBUG settings that no longer exist, drop the leak-profiler experiment flag, and run Mac builds on macOS 13 or later. That’s why my default advice for anyone still on 1.25 is to skip 1.26 and go straight to 1.27. Stay on 1.26.x only if one of those chores blocks you before Go 1.28 arrives. If you’re wondering how Go’s performance stacks up against Rust for backend work, our Rust vs Go comparison has the latest benchmarks and salary data.