Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Go runtime/debug 包详解

概述

runtime/debug 包包含程序在运行时进行自我调试的工具。

重要说明

  • 提供垃圾回收统计信息
  • 支持堆转储和堆栈跟踪
  • 允许调整 GC 和内存限制
  • 用于调试和性能分析场景
  • 大多数功能在生产环境中应谨慎使用

包导入

import "runtime/debug"

基本使用

package main

import (
    "fmt"
    "runtime/debug"
)

func main() {
    // 打印堆栈跟踪
    debug.PrintStack()
    
    // 获取 GC 统计
    var stats debug.GCStats
    debug.ReadGCStats(&stats)
    fmt.Printf("NumGC: %d\n", stats.NumGC)
    
    // 强制释放内存
    debug.FreeOSMemory()
}

运行结果

goroutine 1 [running]:
runtime/debug.Stack()
    /usr/local/go/src/runtime/debug/stack.go:24 +0x65
runtime/debug.PrintStack()
    /usr/local/go/src/runtime/debug/stack.go:16 +0x17
main.main()
    /path/to/main.go:10 +0x19
NumGC: 0

函数详解(按 a-z 排序)

FreeOSMemory

func FreeOSMemory()

说明:强制进行垃圾回收,然后尝试尽可能多地将内存返回给操作系统。

使用示例

package main

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

func main() {
    var m runtime.MemStats
    
    // 分配大量内存
    data := make([]byte, 100*1024*1024) // 100MB
    _ = data
    
    runtime.ReadMemStats(&m)
    fmt.Printf("Before GC: Alloc = %v MB, Sys = %v MB\n", 
        m.Alloc/1024/1024, m.Sys/1024/1024)
    
    // 释放内存
    debug.FreeOSMemory()
    
    time.Sleep(100 * time.Millisecond)
    
    runtime.ReadMemStats(&m)
    fmt.Printf("After GC: Alloc = %v MB, Sys = %v MB\n", 
        m.Alloc/1024/1024, m.Sys/1024/1024)
}

运行结果

Before GC: Alloc = 100 MB, Sys = 107 MB
After GC: Alloc = 0 MB, Sys = 107 MB

PrintStack

func PrintStack()

说明:将 runtime.Stack 返回的堆栈跟踪打印到标准错误。

使用示例

package main

import (
    "runtime/debug"
)

func level1() {
    level2()
}

func level2() {
    level3()
}

func level3() {
    debug.PrintStack()
}

func main() {
    level1()
}

运行结果

goroutine 1 [running]:
runtime/debug.Stack()
    /usr/local/go/src/runtime/debug/stack.go:24 +0x65
runtime/debug.PrintStack()
    /usr/local/go/src/runtime/debug/stack.go:16 +0x17
main.level3(...)
    main.go:14
main.level2()
    main.go:10 +0x25
main.level1()
    main.go:6 +0x25
main.main()
    main.go:18 +0x25

ReadGCStats

func ReadGCStats(stats *GCStats)

说明:读取垃圾回收统计信息到 stats。

使用示例

package main

import (
    "fmt"
    "runtime/debug"
)

func main() {
    var stats debug.GCStats
    
    // 读取 GC 统计
    debug.ReadGCStats(&stats)
    
    fmt.Printf("NumGC: %d\n", stats.NumGC)
    fmt.Printf("LastGC: %v\n", stats.LastGC)
    fmt.Printf("PauseTotal: %v\n", stats.PauseTotal)
    
    if len(stats.Pause) > 0 {
        fmt.Printf("Recent pauses: %v\n", stats.Pause)
    }
}

运行结果

NumGC: 0
LastGC: 0001-01-01 00:00:00 +0000 UTC
PauseTotal: 0s

SetCrashOutput

func SetCrashOutput(f *os.File, opts CrashOptions) error

说明:配置额外的文件来打印未处理的 panic 和其他致命错误。

使用示例

package main

import (
    "log"
    "os"
    "runtime/debug"
)

func main() {
    // 设置崩溃输出文件
    f, err := os.Create("crash.log")
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()
    
    err = debug.SetCrashOutput(f, debug.CrashOptions{})
    if err != nil {
        log.Fatal(err)
    }
    
    // 现在 panic 会写入 crash.log
    panic("test crash")
}

SetGCPercent

func SetGCPercent(percent int) int

说明:设置垃圾回收目标百分比。

使用示例

package main

import (
    "fmt"
    "runtime/debug"
)

func main() {
    // 获取当前设置
    old := debug.SetGCPercent(100)
    fmt.Printf("Old GOGC: %d\n", old)
    
    // 设置为 50%(更频繁的 GC)
    old = debug.SetGCPercent(50)
    fmt.Printf("Changed to: %d\n", old)
    
    // 禁用 GC(不推荐)
    // old = debug.SetGCPercent(-1)
}

运行结果

Old GOGC: 100
Changed to: 100

SetMaxStack

func SetMaxStack(bytes int) int

说明:设置单个 goroutine 堆栈可以使用的最大内存量。

使用示例

package main

import (
    "fmt"
    "runtime/debug"
)

func main() {
    // 获取当前限制
    old := debug.SetMaxStack(1024 * 1024) // 1MB
    fmt.Printf("Old max stack: %d bytes\n", old)
    
    // 设置为 512KB
    old = debug.SetMaxStack(512 * 1024)
    fmt.Printf("New max stack: %d bytes\n", old)
    
    // 恢复默认
    debug.SetMaxStack(1024 * 1024 * 1024) // 1GB
}

运行结果

Old max stack: 1073741824 bytes
New max stack: 1048576 bytes

SetMaxThreads

func SetMaxThreads(threads int) int

说明:设置 Go 程序可以使用的最大操作系统线程数。

使用示例

package main

import (
    "fmt"
    "runtime/debug"
)

func main() {
    // 获取当前限制
    old := debug.SetMaxThreads(1000)
    fmt.Printf("Old max threads: %d\n", old)
    
    // 设置为 500
    old = debug.SetMaxThreads(500)
    fmt.Printf("New max threads: %d\n", old)
    
    // 恢复默认
    debug.SetMaxThreads(10000)
}

运行结果

Old max threads: 10000
New max threads: 1000

SetMemoryLimit

func SetMemoryLimit(limit int64) int64

说明:为运行时提供软内存限制。

使用示例

package main

import (
    "fmt"
    "math"
    "runtime/debug"
)

func main() {
    // 获取当前限制
    old := debug.SetMemoryLimit(100 * 1024 * 1024) // 100MB
    fmt.Printf("Old memory limit: %d bytes\n", old)
    
    // 设置为 50MB
    old = debug.SetMemoryLimit(50 * 1024 * 1024)
    fmt.Printf("New memory limit: %d bytes\n", old)
    
    // 禁用限制
    debug.SetMemoryLimit(math.MaxInt64)
}

运行结果

Old memory limit: 9223372036854775807 bytes
New memory limit: 104857600 bytes

SetPanicOnFault

func SetPanicOnFault(enabled bool) bool

说明:控制运行时在程序在非空地址发生故障时的行为。

使用示例

package main

import (
    "fmt"
    "runtime/debug"
)

func main() {
    // 启用 panic on fault
    old := debug.SetPanicOnFault(true)
    fmt.Printf("Old setting: %v\n", old)
    
    // 注意:这需要实际的内存故障来测试
    // 通常用于内存映射文件等场景
    
    // 恢复默认
    debug.SetPanicOnFault(false)
}

SetTraceback

func SetTraceback(level string)

说明:设置运行时在打印堆栈跟踪时的详细程度。

使用示例

package main

import (
    "runtime/debug"
)

func main() {
    // 设置详细级别
    debug.SetTraceback("all")  // 打印所有 goroutine
    // debug.SetTraceback("system")  // 包括运行时函数
    // debug.SetTraceback("crash")   // 崩溃时生成 core dump
    
    // 触发 panic 来测试
    panic("test")
}

Stack

func Stack() []byte

说明:返回调用它的 goroutine 的格式化堆栈跟踪。

使用示例

package main

import (
    "fmt"
    "runtime/debug"
)

func level1() {
    level2()
}

func level2() {
    level3()
}

func level3() {
    stack := debug.Stack()
    fmt.Printf("Stack trace (%d bytes):\n%s", len(stack), stack)
}

func main() {
    level1()
}

运行结果

Stack trace (xxx bytes):
goroutine 1 [running]:
runtime/debug.Stack()
    /usr/local/go/src/runtime/debug/stack.go:24 +0x65
main.level3(...)
    main.go:14
main.level2()
    main.go:10 +0x25
main.level1()
    main.go:6 +0x25
main.main()
    main.go:18 +0x25

WriteHeapDump

func WriteHeapDump(fd uintptr)

说明:将堆和其中对象的描述写入给定的文件描述符。

使用示例

package main

import (
    "fmt"
    "os"
    "runtime/debug"
    "syscall"
)

func main() {
    // 创建临时文件
    f, err := os.CreateTemp("", "heapdump")
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    defer os.Remove(f.Name())
    
    // 获取文件描述符
    fd := uintptr(f.SyscallConn().(syscall.Conn).RawConn())
    
    // 写入堆转储
    debug.WriteHeapDump(fd)
    
    fmt.Printf("Heap dump written to %s\n", f.Name())
}

类型详解

BuildInfo

type BuildInfo struct {
    GoVersion string
    Path      string
    Main      Module
    Deps      []*Module
    Settings  []BuildSetting
}

说明:表示从 Go 二进制文件读取的构建信息。

ParseBuildInfo

func ParseBuildInfo(data string) (bi *BuildInfo, err error)

说明:解析 *BuildInfo.String 返回的字符串。

使用示例

package main

import (
    "fmt"
    "runtime/debug"
)

func main() {
    info, ok := debug.ReadBuildInfo()
    if !ok {
        fmt.Println("No build info")
        return
    }
    
    // 转换为字符串再解析
    str := info.String()
    parsed, err := debug.ParseBuildInfo(str)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    
    fmt.Printf("Go Version: %s\n", parsed.GoVersion)
    fmt.Printf("Path: %s\n", parsed.Path)
}

ReadBuildInfo

func ReadBuildInfo() (info *BuildInfo, ok bool)

说明:返回嵌入在运行二进制文件中的构建信息。

使用示例

package main

import (
    "fmt"
    "runtime/debug"
)

func main() {
    info, ok := debug.ReadBuildInfo()
    if !ok {
        fmt.Println("No build info available")
        return
    }
    
    fmt.Printf("Go Version: %s\n", info.GoVersion)
    fmt.Printf("Path: %s\n", info.Path)
    fmt.Printf("Main Module: %s@%s\n", info.Main.Path, info.Main.Version)
    
    if len(info.Deps) > 0 {
        fmt.Printf("Dependencies: %d\n", len(info.Deps))
        for i, dep := range info.Deps {
            if i < 5 {
                fmt.Printf("  - %s@%s\n", dep.Path, dep.Version)
            }
        }
    }
    
    fmt.Printf("Settings: %d\n", len(info.Settings))
    for _, s := range info.Settings {
        fmt.Printf("  %s = %s\n", s.Key, s.Value)
    }
}

运行结果

Go Version: go1.21.0
Path: command-line-arguments
Main Module: 
Dependencies: 0
Settings: 6
  -buildmode = exe
  compiler = gc
  CGO_ENABLED = 1
  GOARCH = amd64
  GOOS = windows
  vcs = git

String

func (bi *BuildInfo) String() string

说明:返回 BuildInfo 的字符串表示。

使用示例

package main

import (
    "fmt"
    "runtime/debug"
)

func main() {
    info, ok := debug.ReadBuildInfo()
    if !ok {
        return
    }
    
    fmt.Println(info.String())
}

运行结果

go 1.21.0
path    command-line-arguments
dep     github.com/example/module v1.0.0  h1:xxx=
build   -buildmode=exe
build   compiler=gc
build   GOARCH=amd64
build   GOOS=windows

BuildSetting

type BuildSetting struct {
    Key   string
    Value string
}

说明:键值对,描述影响构建的一个设置。

定义的键

  • -buildmode - 构建模式
  • -compiler - 编译器工具链
  • CGO_ENABLED - CGO 启用状态
  • CGO_CFLAGS - CGO C 标志
  • GOARCH - 目标架构
  • GOOS - 目标操作系统
  • vcs - 版本控制系统
  • vcs.revision - 修订标识符
  • vcs.time - 修改时间
  • vcs.modified - 是否有本地修改

CrashOptions

type CrashOptions struct{}

说明:控制致命崩溃消息格式的选项。

GCStats

type GCStats struct {
    NumGC       int64
    LastGC      time.Time
    PauseTotal  time.Duration
    Pause       []time.Duration
    PauseEnd    []time.Time
    NumForced   int64
}

说明:收集有关最近垃圾回收的信息。

使用示例

package main

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

func main() {
    var stats debug.GCStats
    
    // 进行几次 GC
    for i := 0; i < 3; i++ {
        data := make([]byte, 10*1024*1024)
        _ = data
        time.Sleep(10 * time.Millisecond)
    }
    
    // 读取统计
    debug.ReadGCStats(&stats)
    
    fmt.Printf("NumGC: %d\n", stats.NumGC)
    fmt.Printf("NumForced: %d\n", stats.NumForced)
    fmt.Printf("LastGC: %v\n", stats.LastGC)
    fmt.Printf("PauseTotal: %v\n", stats.PauseTotal)
    
    if len(stats.Pause) > 0 {
        fmt.Printf("Recent pauses: %v\n", stats.Pause)
    }
}

运行结果

NumGC: 3
NumForced: 0
LastGC: 2024-01-15 10:30:45.123456789 +0800 CST
PauseTotal: 1.234ms
Recent pauses: [0.456ms 0.345ms 0.433ms]

Module

type Module struct {
    Path    string
    Version string
    Sum     string
    Replace *Module
}

说明:描述构建中包含的单个模块。

使用示例

package main

import (
    "fmt"
    "runtime/debug"
)

func main() {
    info, ok := debug.ReadBuildInfo()
    if !ok {
        return
    }
    
    fmt.Printf("Main Module:\n")
    fmt.Printf("  Path: %s\n", info.Main.Path)
    fmt.Printf("  Version: %s\n", info.Main.Version)
    
    if len(info.Deps) > 0 {
        fmt.Printf("\nDependencies:\n")
        for _, dep := range info.Deps {
            fmt.Printf("  %s@%s\n", dep.Path, dep.Version)
            if dep.Replace != nil {
                fmt.Printf("    => %s@%s\n", dep.Replace.Path, dep.Replace.Version)
            }
        }
    }
}

典型示例

示例 1:内存监控

package main

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

func printMemStats(label string) {
    var m runtime.MemStats
    runtime.ReadMemStats(&m)
    
    var gcStats debug.GCStats
    debug.ReadGCStats(&gcStats)
    
    fmt.Printf("%s:\n", label)
    fmt.Printf("  Alloc = %v KB\n", m.Alloc/1024)
    fmt.Printf("  Sys = %v KB\n", m.Sys/1024)
    fmt.Printf("  NumGC = %v\n", m.NumGC)
    fmt.Printf("  GC Pause Total = %v\n", gcStats.PauseTotal)
    fmt.Println()
}

func main() {
    printMemStats("Initial")
    
    // 分配内存
    data := make([][]byte, 100)
    for i := range data {
        data[i] = make([]byte, 10*1024)
    }
    
    printMemStats("After allocation")
    
    // 释放
    data = nil
    debug.FreeOSMemory()
    
    time.Sleep(100 * time.Millisecond)
    printMemStats("After FreeOSMemory")
}

示例 2:调试 Panic

package main

import (
    "fmt"
    "runtime/debug"
)

func recoverPanic() {
    if r := recover(); r != nil {
        fmt.Println("Recovered from panic:", r)
        fmt.Println("\nStack trace:")
        fmt.Println(string(debug.Stack()))
    }
}

func level1() {
    level2()
}

func level2() {
    level3()
}

func level3() {
    panic("something went wrong")
}

func main() {
    defer recoverPanic()
    level1()
}

运行结果

Recovered from panic: something went wrong

Stack trace:
goroutine 1 [running]:
runtime/debug.Stack()
    /usr/local/go/src/runtime/debug/stack.go:24 +0x65
main.recoverPanic()
    main.go:11 +0x56
panic(...)
    /usr/local/go/src/runtime/panic.go:xxx
main.level3()
    main.go:22 +0x25
main.level2()
    main.go:18 +0x25
main.level1()
    main.go:14 +0x25
main.main()
    main.go:27 +0x3d

示例 3:GC 调优

package main

import (
    "fmt"
    "runtime"
    "runtime/debug"
)

func main() {
    // 查看当前设置
    fmt.Printf("Current GOGC: %d\n", debug.SetGCPercent(100))
    fmt.Printf("Current GOMAXPROCS: %d\n", runtime.GOMAXPROCS(0))
    
    // 调整为更积极的 GC(减少内存使用)
    old := debug.SetGCPercent(50)
    fmt.Printf("Changed GOGC from %d to 50\n", old)
    
    // 或调整为更宽松的 GC(提高性能)
    // debug.SetGCPercent(200)
}

示例 4:构建信息查看器

package main

import (
    "encoding/json"
    "fmt"
    "runtime/debug"
)

func main() {
    info, ok := debug.ReadBuildInfo()
    if !ok {
        fmt.Println("No build info")
        return
    }
    
    // 打印 JSON 格式
    jsonData, _ := json.MarshalIndent(info, "", "  ")
    fmt.Println(string(jsonData))
}

示例 5:堆栈分析工具

package main

import (
    "fmt"
    "runtime/debug"
    "strings"
)

func analyzeStack() {
    stack := debug.Stack()
    lines := strings.Split(string(stack), "\n")
    
    fmt.Printf("Stack has %d lines\n", len(lines))
    
    // 统计 goroutine 数量
    goroutines := 0
    for _, line := range lines {
        if strings.HasPrefix(line, "goroutine ") {
            goroutines++
        }
    }
    fmt.Printf("Goroutines: %d\n", goroutines)
}

func main() {
    analyzeStack()
}

示例 6:内存限制控制

package main

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

func main() {
    // 设置内存限制为 50MB
    old := debug.SetMemoryLimit(50 * 1024 * 1024)
    fmt.Printf("Old memory limit: %d MB\n", old/1024/1024)
    
    // 监控内存使用
    ticker := time.NewTicker(100 * time.Millisecond)
    defer ticker.Stop()
    
    for i := 0; i < 5; i++ {
        <-ticker.C
        
        var m runtime.MemStats
        runtime.ReadMemStats(&m)
        
        fmt.Printf("[%d] Alloc = %v MB, Sys = %v MB\n", 
            i, m.Alloc/1024/1024, m.Sys/1024/1024)
    }
    
    // 恢复无限制
    debug.SetMemoryLimit(9223372036854775807)
}

示例 7:崩溃日志记录

package main

import (
    "log"
    "os"
    "runtime/debug"
)

func setupCrashLog() {
    f, err := os.OpenFile("crash.log", os.O_CREATE|os.O_WRONLY, 0644)
    if err != nil {
        log.Fatal(err)
    }
    
    err = debug.SetCrashOutput(f, debug.CrashOptions{})
    if err != nil {
        log.Fatal(err)
    }
}

func main() {
    setupCrashLog()
    
    // 现在 panic 会记录到 crash.log
    defer func() {
        if r := recover(); r != nil {
            log.Printf("Recovered: %v", r)
        }
    }()
    
    panic("test crash")
}

示例 8:性能分析辅助

package main

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

func benchmark(label string, fn func()) {
    var m1, m2 runtime.MemStats
    runtime.ReadMemStats(&m1)
    
    start := time.Now()
    fn()
    elapsed := time.Since(start)
    
    runtime.ReadMemStats(&m2)
    
    fmt.Printf("%s:\n", label)
    fmt.Printf("  Time: %v\n", elapsed)
    fmt.Printf("  Alloc: %v KB\n", (m2.Alloc-m1.Alloc)/1024)
    fmt.Printf("  NumGC: %d\n", m2.NumGC-m1.NumGC)
    fmt.Println()
}

func main() {
    benchmark("Without FreeOSMemory", func() {
        data := make([][]byte, 1000)
        for i := range data {
            data[i] = make([]byte, 1024)
        }
    })
    
    benchmark("With FreeOSMemory", func() {
        data := make([][]byte, 1000)
        for i := range data {
            data[i] = make([]byte, 1024)
        }
        debug.FreeOSMemory()
    })
}

最佳实践

1. 谨慎使用 FreeOSMemory

// ✅ 推荐:在特定时机调用
func processLargeData() {
    // 处理大数据
    // ...
    
    // 完成后释放
    debug.FreeOSMemory()
}

// ❌ 不推荐:频繁调用
for i := 0; i < 1000; i++ {
    process()
    debug.FreeOSMemory()  // 影响性能
}

2. 合理设置 GC 参数

// ✅ 推荐:根据应用特点调整
func init() {
    // 低延迟应用:更频繁的 GC
    debug.SetGCPercent(50)
}

// ❌ 不推荐:禁用 GC
debug.SetGCPercent(-1)  // 可能导致 OOM

3. 使用 ReadBuildInfo 获取版本信息

// ✅ 推荐:在启动时记录
func main() {
    if info, ok := debug.ReadBuildInfo(); ok {
        log.Printf("Version: %s", info.Main.Version)
    }
}

4. 设置合理的堆栈限制

// ✅ 推荐:防止无限递归
func init() {
    debug.SetMaxStack(256 * 1024 * 1024) // 256MB
}

与其他包配合

runtime 包

package main

import (
    "fmt"
    "runtime"
    "runtime/debug"
)

func main() {
    var m runtime.MemStats
    var gc debug.GCStats
    
    runtime.ReadMemStats(&m)
    debug.ReadGCStats(&gc)
    
    fmt.Printf("Memory: %d KB, GC: %d times\n", 
        m.Alloc/1024, gc.NumGC)
}

log 包

package main

import (
    "log"
    "runtime/debug"
)

func main() {
    defer func() {
        if r := recover(); r != nil {
            log.Printf("Panic: %v\n%s", r, debug.Stack())
        }
    }()
    
    panic("test")
}

快速参考

函数

函数参数返回值说明
FreeOSMemory--释放内存给 OS
PrintStack--打印堆栈跟踪
ReadGCStatsstats *GCStats-读取 GC 统计
SetCrashOutputf *os.File, opts CrashOptionserror设置崩溃输出
SetGCPercentpercent intint设置 GC 百分比
SetMaxStackbytes intint设置最大堆栈
SetMaxThreadsthreads intint设置最大线程数
SetMemoryLimitlimit int64int64设置内存限制
SetPanicOnFaultenabled boolbool设置 panic on fault
SetTracebacklevel string-设置堆栈详细度
Stack-[]byte获取堆栈跟踪
WriteHeapDumpfd uintptr-写入堆转储

类型

类型说明
BuildInfo构建信息
BuildSetting构建设置键值对
CrashOptions崩溃输出选项
GCStatsGC 统计信息
Module模块描述

注意事项

1. 性能影响

  • FreeOSMemory 会触发 GC,影响性能
  • 频繁的 GC 统计读取有开销
  • 堆栈跟踪生成是昂贵操作

2. 生产环境使用

  • 谨慎调整 GC 参数
  • 避免在生产环境频繁调用调试函数
  • SetCrashOutput 可能泄露敏感信息

3. 资源管理

  • WriteHeapDump 会暂停所有 goroutine
  • 确保文件描述符有效
  • 使用临时文件存储堆转储

4. 平台差异

  • 某些功能在 Windows 上行为不同
  • 内存释放效果因平台而异

5. 版本兼容

  • BuildInfo 格式可能随 Go 版本变化
  • 某些字段在旧版本中不可用

总结

runtime/debug 包提供了程序运行时调试的工具。

核心要点

  1. FreeOSMemory 会触发 GC,应谨慎使用
  2. SetGCPercent 和 SetMemoryLimit 可用于调优 GC
  3. ReadBuildInfo 提供构建信息
  4. Stack 和 PrintStack 用于调试 panic
  5. 生产环境应谨慎使用调试功能

主要用途

  • 性能分析和调优
  • 内存使用监控
  • panic 调试和日志记录
  • 构建信息获取
  • GC 行为调优