Runtime#

The Go runtime is the scheduler, the garbage collector, the allocator, and the standard libraries linked into every binary. The compiler (go itself wraps it) produces a single static executable; no separate VM, no shared libraries, no LD_LIBRARY_PATH to fight.

For the toolchain that drives the runtime (go, gofmt, go vet, dlv), see Tooling.

The toolchain#

go is the one front-end command. The subcommands the operator uses daily.

Command

Effect

go build

Compile the current module to a single binary.

go run main.go

Compile and run; no binary kept.

go test

Run tests in the current module.

go mod init <name>

Initialise a new module.

go mod tidy

Add missing and drop unused module requirements.

go get pkg@v1

Add or update a dependency.

go vet ./...

Static checks for likely bugs.

gofmt -w .

Format every file in place.

go doc <pkg>

Print docs for a package.

Modules#

A module is a unit of versioned source; the boundary is go.mod at its root.

$ go mod init github.com/operator/scan
$ go get github.com/spf13/cobra@latest
$ go mod tidy

go.mod records the module path, the Go version, and direct dependencies; go.sum records hash digests for every transitive dependency. Both files are committed.

module github.com/operator/scan

go 1.22

require (
    github.com/spf13/cobra v1.8.0
    golang.org/x/sync v0.7.0
)

Module paths are URLs (github.com/operator/scan, golang.org/x/sync); the toolchain resolves them through the module proxy (proxy.golang.org) by default.

Imports#

import "path" brings a package into scope. The package name is the import’s last segment by convention; an alias can override.

import (
    "fmt"
    "os"

    "github.com/spf13/cobra"
    "github.com/jackc/pgx/v5"

    sqld "github.com/some/other/sqlite/v3"   // alias to avoid collision
    _ "github.com/lib/pq"                     // side-effect import (init only)
)

The blank-identifier import is the operator’s hook for registration patterns (database drivers, image decoders).

Cross-compilation#

The Go toolchain cross-compiles natively. GOOS picks the target OS; GOARCH picks the architecture.

$ GOOS=linux  GOARCH=amd64 go build -o scan-linux-amd64
$ GOOS=darwin GOARCH=arm64 go build -o scan-darwin-arm64
$ GOOS=windows GOARCH=amd64 go build -o scan.exe

Common matrix.

GOOS

GOARCH

Notes

linux

amd64

Standard Linux x86-64.

linux

arm64

Servers (Graviton, Ampere), Raspberry Pi 4/5.

darwin

arm64

Apple Silicon.

darwin

amd64

Intel macOS.

windows

amd64

Standard Windows.

freebsd, openbsd

amd64

BSDs.

CGO_ENABLED=0 produces a fully static binary (no libc); the operator sets it for binaries that need to run inside scratch containers or on a target without matching glibc.

$ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
    go build -ldflags="-s -w" -o scan

-s -w strips debug info; the binary shrinks noticeably.

Build tags#

Conditional compilation through file-name suffixes (_linux.go, _amd64.go, _test.go) or //go:build directives.

//go:build linux

package scan

func platformProbe() { /* linux-specific */ }

Workspaces#

go.work lets the operator develop multiple modules together without publishing intermediate versions.

$ go work init ./scanner ./shared

The workspace overrides module resolution for the listed modules; useful in monorepos.

Scheduler#

Go’s runtime schedules goroutines (M) onto OS threads (N) using a work-stealing scheduler. GOMAXPROCS caps the number of OS threads running Go code concurrently; the default is the number of CPU cores.

$ GOMAXPROCS=4 ./service

The operator rarely tunes this; the default is right.

Garbage collection#

A concurrent tri-colour mark-sweep collector. It does not call the GC; runtime.GC() exists for tests. GOGC controls the trigger threshold (default 100 = collect when heap doubles); GOMEMLIMIT sets a soft memory cap (Go 1.19+).

$ GOGC=200 ./service          # collect less often, use more RAM
$ GOMEMLIMIT=2GiB ./service   # cap heap+stacks+other at 2GB

runtime package#

The runtime package exposes hooks use for profiling and debugging.

import "runtime"

runtime.NumCPU()                  // logical CPUs
runtime.NumGoroutine()            // live goroutines (debug)
runtime.GOOS                      // "linux"
runtime.GOARCH                    // "amd64"
runtime.Version()                 // "go1.22.3"

var ms runtime.MemStats
runtime.ReadMemStats(&ms)
fmt.Println(ms.HeapAlloc, ms.NumGC)

For profiling, net/http/pprof (live HTTP server) or runtime/pprof (offline files); go tool pprof reads both.

Reflection#

reflect exposes type and value introspection at runtime. Used by encoding/json, fmt, and generic libraries that predate Go generics.

import "reflect"

t := reflect.TypeOf(x)
v := reflect.ValueOf(x)
for i := 0; i < t.NumField(); i++ {
    fmt.Println(t.Field(i).Name, v.Field(i).Interface())
}

Avoid reflection in hot paths; the compiler generates faster code with explicit types or generics.

unsafe#

unsafe.Pointer bridges to type-erased pointers; used inside runtime, reflect, and a handful of high-performance libraries. Do not reach for unsafe in application code; the cost is loss of type safety and possibly forward-compatibility.

cgo#

import "C" calls C code from Go. Expensive in build complexity, cross-compilation, and runtime cost; the operator avoids it unless an existing native library is the only path.

References#