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/metrics 包详解

概述

runtime/metrics 包提供了访问 Go 运行时导出的实现定义指标的稳定接口。它类似于现有的 runtime.ReadMemStatsruntime/debug.ReadGCStats 函数,但更加通用。

重要说明

  • 提供统一的指标访问接口
  • 指标集合可能随运行时演化而变化
  • 支持跨不同 Go 实现的变体
  • 指标通过字符串键标识
  • 每种指标都有“kind“(值类型)

与现有 API 的区别

  • runtime.ReadMemStats - 固定的内存统计结构
  • runtime/debug.ReadGCStats - 固定的 GC 统计结构
  • runtime/metrics.Read - 通用的动态指标访问

包导入

import "runtime/metrics"

基本使用

package main

import (
    "fmt"
    "runtime/metrics"
)

func main() {
    // 获取所有支持的指标描述
    descs := metrics.All()
    fmt.Printf("Supported metrics: %d\n", len(descs))
    
    // 读取特定指标
    samples := make([]metrics.Sample, 3)
    samples[0].Name = "/gc/heap/allocs:bytes"
    samples[1].Name = "/gc/heap/objects:objects"
    samples[2].Name = "/sched/goroutines:goroutines"
    
    metrics.Read(samples)
    
    for _, s := range samples {
        fmt.Printf("%s = %v\n", s.Name, s.Value)
    }
}

运行结果

Supported metrics: 83
/gc/heap/allocs:bytes = 1234567
/gc/heap/objects:objects = 5678
/sched/goroutines:goroutines = 5

函数详解

Read

func Read(m []Sample)

说明:填充给定指标样本切片中的每个 Value 字段。

使用示例

package main

import (
    "fmt"
    "runtime/metrics"
)

func main() {
    // 创建样本切片
    samples := make([]metrics.Sample, 2)
    samples[0].Name = "/gc/cycles/total:gc-cycles"
    samples[1].Name = "/memory/classes/total:bytes"
    
    // 读取指标值
    metrics.Read(samples)
    
    // 打印结果
    for _, s := range samples {
        switch s.Value.Kind() {
        case metrics.KindUint64:
            fmt.Printf("%s = %d\n", s.Name, s.Value.Uint64())
        case metrics.KindFloat64:
            fmt.Printf("%s = %f\n", s.Name, s.Value.Float64())
        case metrics.KindFloat64Histogram:
            fmt.Printf("%s = %v\n", s.Name, s.Value.Float64Histogram())
        }
    }
}

运行结果

/gc/cycles/total:gc-cycles = 10
/memory/classes/total:bytes = 8388608

批量读取示例

package main

import (
    "fmt"
    "runtime/metrics"
)

func readAllMetrics() {
    descs := metrics.All()
    samples := make([]metrics.Sample, len(descs))
    
    for i, desc := range descs {
        samples[i].Name = desc.Name
    }
    
    metrics.Read(samples)
    
    for _, s := range samples {
        if s.Value.Kind() != metrics.KindBad {
            fmt.Printf("%-50s = %v\n", s.Name, s.Value)
        }
    }
}

func main() {
    readAllMetrics()
}

重用切片示例

package main

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

func main() {
    // 预分配切片并重复使用
    samples := []metrics.Sample{
        {Name: "/sched/goroutines:goroutines"},
        {Name: "/gc/heap/allocs:bytes"},
    }
    
    // 定期读取
    for i := 0; i < 5; i++ {
        metrics.Read(samples)
        
        goroutines := samples[0].Value.Uint64()
        allocs := samples[1].Value.Uint64()
        
        fmt.Printf("[%d] Goroutines: %d, Allocs: %d bytes\n", 
            i, goroutines, allocs)
        
        time.Sleep(100 * time.Millisecond)
    }
}

运行结果

[0] Goroutines: 1, Allocs: 1234 bytes
[1] Goroutines: 1, Allocs: 1234 bytes
[2] Goroutines: 1, Allocs: 1234 bytes
[3] Goroutines: 1, Allocs: 1234 bytes
[4] Goroutines: 1, Allocs: 1234 bytes

类型详解

Description

type Description struct {
    Name        string
    Description string
    Kind        ValueKind
    Cumulative  bool
    Unit        string
}

说明:描述运行时指标。

All

func All() []Description

说明:返回包含所有支持指标的描述切片。

使用示例

package main

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

func main() {
    descs := metrics.All()
    
    fmt.Printf("Total metrics: %d\n\n", len(descs))
    
    // 按类别分组显示
    categories := make(map[string]int)
    for _, desc := range descs {
        parts := strings.Split(desc.Name, "/")
        if len(parts) > 1 {
            category := parts[1]
            categories[category]++
        }
    }
    
    fmt.Println("Categories:")
    for cat, count := range categories {
        fmt.Printf("  /%s/* : %d metrics\n", cat, count)
    }
    
    // 显示部分指标详情
    fmt.Println("\nSample metrics:")
    for i := 0; i < 5 && i < len(descs); i++ {
        desc := descs[i]
        fmt.Printf("\nName: %s\n", desc.Name)
        fmt.Printf("Description: %s\n", desc.Description)
        fmt.Printf("Kind: %v\n", desc.Kind)
        fmt.Printf("Cumulative: %v\n", desc.Cumulative)
        fmt.Printf("Unit: %s\n", desc.Unit)
    }
}

运行结果

Total metrics: 83

Categories:
  /cpu/classes/* : 11 metrics
  /gc/* : 25 metrics
  /godebug/* : 30 metrics
  /memory/classes/* : 13 metrics
  /sched/* : 9 metrics

Sample metrics:

Name: /cgo/go-to-c-calls:calls
Description: Count of calls made from Go to C by the current process.
Kind: 1
Cumulative: true
Unit: calls

Float64Histogram

type Float64Histogram struct {
    Counts  []uint64
    Buckets []float64
}

说明:表示 float64 值的分布。

字段说明

  • Counts - 每个桶的计数
  • Buckets - 桶边界(单调递增)

使用示例

package main

import (
    "fmt"
    "runtime/metrics"
)

func main() {
    // 读取直方图指标
    samples := []metrics.Sample{
        {Name: "/gc/pauses:seconds"},
        {Name: "/sched/latencies:seconds"},
    }
    
    metrics.Read(samples)
    
    for _, s := range samples {
        if s.Value.Kind() == metrics.KindFloat64Histogram {
            hist := s.Value.Float64Histogram()
            
            fmt.Printf("\n%s:\n", s.Name)
            fmt.Printf("Buckets: %v\n", hist.Buckets)
            fmt.Printf("Counts: %v\n", hist.Counts)
            
            // 计算总数
            var total uint64
            for _, count := range hist.Counts {
                total += count
            }
            fmt.Printf("Total samples: %d\n", total)
        }
    }
}

Sample

type Sample struct {
    Name  string
    Value Value
}

说明:捕获单个指标样本。

使用示例

package main

import (
    "fmt"
    "runtime/metrics"
)

func main() {
    // 创建样本
    sample := metrics.Sample{
        Name: "/sched/goroutines:goroutines",
    }
    
    // 读取值
    metrics.Read([]metrics.Sample{sample})
    
    fmt.Printf("Name: %s\n", sample.Name)
    fmt.Printf("Kind: %v\n", sample.Value.Kind())
    fmt.Printf("Value: %d\n", sample.Value.Uint64())
}

Value

type Value struct{}

说明:表示运行时返回的指标值。

Float64

func (v Value) Float64() float64

说明:返回指标的 float64 值。如果 Kind 不是 KindFloat64 会 panic。

使用示例

package main

import (
    "fmt"
    "runtime/metrics"
)

func main() {
    samples := []metrics.Sample{
        {Name: "/cpu/classes/user:cpu-seconds"},
    }
    
    metrics.Read(samples)
    
    value := samples[0].Value
    if value.Kind() == metrics.KindFloat64 {
        fmt.Printf("CPU seconds: %f\n", value.Float64())
    }
}

Float64Histogram

func (v Value) Float64Histogram() *Float64Histogram

说明:返回指标的 *Float64Histogram 值。如果 Kind 不是 KindFloat64Histogram 会 panic。

使用示例

package main

import (
    "fmt"
    "runtime/metrics"
)

func main() {
    samples := []metrics.Sample{
        {Name: "/sched/pauses/total/gc:seconds"},
    }
    
    metrics.Read(samples)
    
    value := samples[0].Value
    if value.Kind() == metrics.KindFloat64Histogram {
        hist := value.Float64Histogram()
        fmt.Printf("GC pause histogram: %+v\n", hist)
    }
}

Kind

func (v Value) Kind() ValueKind

说明:返回表示值类型的标签。

使用示例

package main

import (
    "fmt"
    "runtime/metrics"
)

func main() {
    samples := []metrics.Sample{
        {Name: "/gc/heap/allocs:bytes"},
        {Name: "/cpu/classes/user:cpu-seconds"},
        {Name: "/sched/pauses/total/gc:seconds"},
    }
    
    metrics.Read(samples)
    
    for _, s := range samples {
        fmt.Printf("%-40s Kind: %v\n", s.Name, s.Value.Kind())
    }
}

运行结果

/gc/heap/allocs:bytes                  Kind: 1
/cpu/classes/user:cpu-seconds          Kind: 2
/sched/pauses/total/gc:seconds         Kind: 3

Uint64

func (v Value) Uint64() uint64

说明:返回指标的 uint64 值。如果 Kind 不是 KindUint64 会 panic。

使用示例

package main

import (
    "fmt"
    "runtime/metrics"
)

func main() {
    samples := []metrics.Sample{
        {Name: "/gc/cycles/total:gc-cycles"},
        {Name: "/sched/goroutines:goroutines"},
    }
    
    metrics.Read(samples)
    
    for _, s := range samples {
        fmt.Printf("%s = %d\n", s.Name, s.Value.Uint64())
    }
}

运行结果

/gc/cycles/total:gc-cycles = 15
/sched/goroutines:goroutines = 3

ValueKind

type ValueKind int

说明:表示指标 Value 类型的标签。

常量

const (
    KindBad          ValueKind = iota  // 未知类型
    KindUint64                         // uint64
    KindFloat64                        // float64
    KindFloat64Histogram               // *Float64Histogram
)

使用示例

package main

import (
    "fmt"
    "runtime/metrics"
)

func main() {
    fmt.Printf("KindBad: %d\n", metrics.KindBad)
    fmt.Printf("KindUint64: %d\n", metrics.KindUint64)
    fmt.Printf("KindFloat64: %d\n", metrics.KindFloat64)
    fmt.Printf("KindFloat64Histogram: %d\n", metrics.KindFloat64Histogram)
}

运行结果

KindBad: 0
KindUint64: 1
KindFloat64: 2
KindFloat64Histogram: 3

支持的指标分类

/cgo/*

  • /cgo/go-to-c-calls:calls - Go 到 C 的调用次数

/cpu/classes/*

  • /cpu/classes/gc/mark/assist:cpu-seconds - GC 辅助 CPU 时间
  • /cpu/classes/gc/mark/dedicated:cpu-seconds - GC 专用 CPU 时间
  • /cpu/classes/gc/mark/idle:cpu-seconds - GC 空闲 CPU 时间
  • /cpu/classes/gc/pause:cpu-seconds - GC 暂停 CPU 时间
  • /cpu/classes/gc/total:cpu-seconds - GC 总 CPU 时间
  • /cpu/classes/idle:cpu-seconds - 空闲 CPU 时间
  • /cpu/classes/scavenge/assist:cpu-seconds - 内存回收辅助 CPU 时间
  • /cpu/classes/scavenge/background:cpu-seconds - 后台内存回收 CPU 时间
  • /cpu/classes/scavenge/total:cpu-seconds - 内存回收总 CPU 时间
  • /cpu/classes/total:cpu-seconds - 总可用 CPU 时间
  • /cpu/classes/user:cpu-seconds - 用户代码 CPU 时间

/gc/*

  • /gc/cleanups/executed:cleanups - 执行的清理函数数
  • /gc/cleanups/queued:cleanups - 排队的清理函数数
  • /gc/cycles/automatic:gc-cycles - 自动 GC 周期数
  • /gc/cycles/forced:gc-cycles - 强制 GC 周期数
  • /gc/cycles/total:gc-cycles - 总 GC 周期数
  • /gc/finalizers/executed:finalizers - 执行的 finalizer 数
  • /gc/finalizers/queued:finalizers - 排队的 finalizer 数
  • /gc/gogc:percent - GC 目标百分比
  • /gc/gomemlimit:bytes - 内存限制
  • /gc/heap/allocs-by-size:bytes - 按大小分布的堆分配
  • /gc/heap/allocs:bytes - 累计堆分配字节数
  • /gc/heap/allocs:objects - 累计堆分配对象数
  • /gc/heap/frees-by-size:bytes - 按大小分布的堆释放
  • /gc/heap/frees:bytes - 累计堆释放字节数
  • /gc/heap/frees:objects - 累计堆释放对象数
  • /gc/heap/goal:bytes - GC 周期结束的堆大小目标
  • /gc/heap/live:bytes - 上一个 GC 标记的存活对象占用的堆内存
  • /gc/heap/objects:objects - 堆中的对象数
  • /gc/heap/tiny/allocs:objects - 小分配数
  • /gc/limiter/last-enabled:gc-cycle - GC CPU 限制器最后启用的周期
  • /gc/pauses:seconds - GC 暂停时间(已弃用)
  • /gc/scan/globals:bytes - 可扫描的全局变量空间
  • /gc/scan/heap:bytes - 可扫描的堆空间
  • /gc/scan/stack:bytes - 上一个 GC 周期扫描的栈字节数
  • /gc/scan/total:bytes - 可扫描的总空间
  • /gc/stack/starting-size:bytes - 新 goroutine 的栈大小

/godebug/non-default-behavior/*

  • 各种 GODEBUG 非默认行为的事件计数(30+ 个指标)

/memory/classes/*

  • /memory/classes/heap/free:bytes - 完全空闲且可返回给系统的内存
  • /memory/classes/heap/objects:bytes - 存活和死亡对象占用的内存
  • /memory/classes/heap/released:bytes - 已返回给系统的空闲内存
  • /memory/classes/heap/stacks:bytes - 为栈预留的堆内存
  • /memory/classes/heap/unused:bytes - 预留但未使用的堆内存
  • /memory/classes/metadata/mcache/free:bytes - 预留但未使用的 mcache 内存
  • /memory/classes/metadata/mcache/inuse:bytes - 正在使用的 mcache 内存
  • /memory/classes/metadata/mspan/free:bytes - 预留但未使用的 mspan 内存
  • /memory/classes/metadata/mspan/inuse:bytes - 正在使用的 mspan 内存
  • /memory/classes/metadata/other:bytes - 运行时元数据内存
  • /memory/classes/os-stacks:bytes - 操作系统分配的栈内存
  • /memory/classes/other:bytes - 其他内存
  • /memory/classes/profiling/buckets:bytes - profiling 使用的内存
  • /memory/classes/total:bytes - 运行时映射的总内存

/sched/*

  • /sched/gomaxprocs:threads - 当前 GOMAXPROCS 设置
  • /sched/goroutines-created:goroutines - 程序启动后创建的 goroutine 数
  • /sched/goroutines/not-in-go:goroutines - 系统调用或 cgo 中的 goroutine 数
  • /sched/goroutines/runnable:goroutines - 准备执行的 goroutine 数
  • /sched/goroutines/running:goroutines - 正在执行的 goroutine 数
  • /sched/goroutines/waiting:goroutines - 等待资源的 goroutine 数
  • /sched/goroutines:goroutines - 存活的 goroutine 数
  • /sched/latencies:seconds - goroutine 调度延迟分布
  • /sched/pauses/stopping/gc:seconds - GC 停止延迟分布
  • /sched/pauses/stopping/other:seconds - 非 GC 停止延迟分布
  • /sched/pauses/total/gc:seconds - GC 暂停延迟分布
  • /sched/pauses/total/other:seconds - 非 GC 暂停延迟分布
  • /sched/threads/total:threads - 运行时拥有的线程数
  • /sync/mutex/wait/total:seconds - goroutine 阻塞在锁上的累计时间

典型示例

示例 1:监控 Goroutine 数量

package main

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

func main() {
    sample := metrics.Sample{
        Name: "/sched/goroutines:goroutines",
    }
    
    for i := 0; i < 10; i++ {
        metrics.Read([]metrics.Sample{sample})
        
        count := sample.Value.Uint64()
        fmt.Printf("[%d] Goroutines: %d\n", i, count)
        
        time.Sleep(100 * time.Millisecond)
    }
}

示例 2:内存使用监控

package main

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

func main() {
    samples := []metrics.Sample{
        {Name: "/memory/classes/total:bytes"},
        {Name: "/memory/classes/heap/objects:bytes"},
        {Name: "/memory/classes/heap/free:bytes"},
        {Name: "/memory/classes/heap/stacks:bytes"},
    }
    
    for i := 0; i < 5; i++ {
        metrics.Read(samples)
        
        total := samples[0].Value.Uint64()
        heapObjects := samples[1].Value.Uint64()
        heapFree := samples[2].Value.Uint64()
        stacks := samples[3].Value.Uint64()
        
        fmt.Printf("[%d] Memory Stats:\n", i)
        fmt.Printf("  Total: %d MB\n", total/1024/1024)
        fmt.Printf("  Heap Objects: %d MB\n", heapObjects/1024/1024)
        fmt.Printf("  Heap Free: %d MB\n", heapFree/1024/1024)
        fmt.Printf("  Stacks: %d MB\n", stacks/1024/1024)
        fmt.Println()
        
        time.Sleep(1 * time.Second)
    }
}

示例 3:GC 活动监控

package main

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

func main() {
    samples := []metrics.Sample{
        {Name: "/gc/cycles/total:gc-cycles"},
        {Name: "/gc/heap/allocs:bytes"},
        {Name: "/gc/heap/frees:bytes"},
        {Name: "/gc/heap/live:bytes"},
        {Name: "/gc/gogc:percent"},
    }
    
    var lastGC uint64
    
    for i := 0; i < 10; i++ {
        metrics.Read(samples)
        
        currentGC := samples[0].Value.Uint64()
        allocs := samples[1].Value.Uint64()
        frees := samples[2].Value.Uint64()
        live := samples[3].Value.Uint64()
        gogc := samples[4].Value.Uint64()
        
        if currentGC > lastGC {
            fmt.Printf("[%d] GC Cycle #%d\n", i, currentGC)
            fmt.Printf("  GOGC: %d%%\n", gogc)
            fmt.Printf("  Allocs: %d bytes\n", allocs)
            fmt.Printf("  Frees: %d bytes\n", frees)
            fmt.Printf("  Live: %d bytes\n", live)
            fmt.Printf("  Net: %d bytes\n", allocs-frees)
            fmt.Println()
            
            lastGC = currentGC
        }
        
        time.Sleep(100 * time.Millisecond)
    }
}

示例 4:CPU 时间分析

package main

import (
    "fmt"
    "runtime/metrics"
)

func main() {
    samples := []metrics.Sample{
        {Name: "/cpu/classes/total:cpu-seconds"},
        {Name: "/cpu/classes/user:cpu-seconds"},
        {Name: "/cpu/classes/gc/total:cpu-seconds"},
        {Name: "/cpu/classes/idle:cpu-seconds"},
    }
    
    metrics.Read(samples)
    
    total := samples[0].Value.Float64()
    user := samples[1].Value.Float64()
    gc := samples[2].Value.Float64()
    idle := samples[3].Value.Float64()
    
    fmt.Printf("CPU Time Analysis:\n")
    fmt.Printf("  Total: %.2f seconds\n", total)
    fmt.Printf("  User:  %.2f seconds (%.1f%%)\n", user, user/total*100)
    fmt.Printf("  GC:    %.2f seconds (%.1f%%)\n", gc, gc/total*100)
    fmt.Printf("  Idle:  %.2f seconds (%.1f%%)\n", idle, idle/total*100)
}

示例 5:调度延迟监控

package main

import (
    "fmt"
    "runtime/metrics"
)

func main() {
    samples := []metrics.Sample{
        {Name: "/sched/latencies:seconds"},
    }
    
    metrics.Read(samples)
    
    hist := samples[0].Value.Float64Histogram()
    
    fmt.Printf("Scheduler Latency Distribution:\n")
    fmt.Printf("Buckets: %v seconds\n", hist.Buckets)
    fmt.Printf("Counts: %v\n", hist.Counts)
    
    // 计算百分位数
    var total uint64
    for _, count := range hist.Counts {
        total += count
    }
    
    if total > 0 {
        fmt.Printf("\nTotal samples: %d\n", total)
        
        // 找到中位数
        var cumulative uint64
        for i, count := range hist.Counts {
            cumulative += count
            if cumulative >= total/2 {
                fmt.Printf("Median latency: < %.6f seconds\n", hist.Buckets[i])
                break
            }
        }
    }
}

示例 6:Finalizer 和 Cleanup 监控

package main

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

func main() {
    samples := []metrics.Sample{
        {Name: "/gc/finalizers/executed:finalizers"},
        {Name: "/gc/finalizers/queued:finalizers"},
        {Name: "/gc/cleanups/executed:cleanups"},
        {Name: "/gc/cleanups/queued:cleanups"},
    }
    
    for i := 0; i < 5; i++ {
        metrics.Read(samples)
        
        finalizersExec := samples[0].Value.Uint64()
        finalizersQueue := samples[1].Value.Uint64()
        cleanupsExec := samples[2].Value.Uint64()
        cleanupsQueue := samples[3].Value.Uint64()
        
        fmt.Printf("[%d] Finalizers: %d executed, %d queued\n", 
            i, finalizersExec, finalizersQueue)
        fmt.Printf("    Cleanups: %d executed, %d queued\n", 
            cleanupsExec, cleanupsQueue)
        fmt.Println()
        
        time.Sleep(100 * time.Millisecond)
    }
}

示例 7:内存分类详细分析

package main

import (
    "fmt"
    "runtime/metrics"
)

func formatBytes(bytes uint64) string {
    const (
        KB = 1024
        MB = 1024 * KB
        GB = 1024 * MB
    )
    
    switch {
    case bytes >= GB:
        return fmt.Sprintf("%.2f GB", float64(bytes)/GB)
    case bytes >= MB:
        return fmt.Sprintf("%.2f MB", float64(bytes)/MB)
    case bytes >= KB:
        return fmt.Sprintf("%.2f KB", float64(bytes)/KB)
    default:
        return fmt.Sprintf("%d B", bytes)
    }
}

func main() {
    samples := []metrics.Sample{
        {Name: "/memory/classes/heap/objects:bytes"},
        {Name: "/memory/classes/heap/free:bytes"},
        {Name: "/memory/classes/heap/released:bytes"},
        {Name: "/memory/classes/heap/stacks:bytes"},
        {Name: "/memory/classes/heap/unused:bytes"},
        {Name: "/memory/classes/metadata/mcache/inuse:bytes"},
        {Name: "/memory/classes/metadata/mspan/inuse:bytes"},
        {Name: "/memory/classes/metadata/other:bytes"},
        {Name: "/memory/classes/os-stacks:bytes"},
        {Name: "/memory/classes/other:bytes"},
        {Name: "/memory/classes/total:bytes"},
    }
    
    metrics.Read(samples)
    
    fmt.Printf("Memory Classification:\n")
    fmt.Printf("  Heap Objects:     %s\n", formatBytes(samples[0].Value.Uint64()))
    fmt.Printf("  Heap Free:        %s\n", formatBytes(samples[1].Value.Uint64()))
    fmt.Printf("  Heap Released:    %s\n", formatBytes(samples[2].Value.Uint64()))
    fmt.Printf("  Heap Stacks:      %s\n", formatBytes(samples[3].Value.Uint64()))
    fmt.Printf("  Heap Unused:      %s\n", formatBytes(samples[4].Value.Uint64()))
    fmt.Printf("  MCache Inuse:     %s\n", formatBytes(samples[5].Value.Uint64()))
    fmt.Printf("  MSpan Inuse:      %s\n", formatBytes(samples[6].Value.Uint64()))
    fmt.Printf("  Metadata Other:   %s\n", formatBytes(samples[7].Value.Uint64()))
    fmt.Printf("  OS Stacks:        %s\n", formatBytes(samples[8].Value.Uint64()))
    fmt.Printf("  Other:            %s\n", formatBytes(samples[9].Value.Uint64()))
    fmt.Printf("  Total:            %s\n", formatBytes(samples[10].Value.Uint64()))
}

示例 8:GODEBUG 行为监控

package main

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

func main() {
    descs := metrics.All()
    
    // 收集所有 godebug 指标
    var godebugSamples []metrics.Sample
    for _, desc := range descs {
        if strings.HasPrefix(desc.Name, "/godebug/") {
            godebugSamples = append(godebugSamples, metrics.Sample{
                Name: desc.Name,
            })
        }
    }
    
    if len(godebugSamples) == 0 {
        fmt.Println("No GODEBUG metrics found")
        return
    }
    
    metrics.Read(godebugSamples)
    
    fmt.Printf("GODEBUG Non-default Behaviors:\n\n")
    
    for _, s := range godebugSamples {
        count := s.Value.Uint64()
        if count > 0 {
            // 提取行为名称
            parts := strings.Split(s.Name, "/")
            name := parts[len(parts)-1]
            name = strings.TrimSuffix(name, ":events")
            
            fmt.Printf("%-40s: %d events\n", name, count)
        }
    }
}

最佳实践

1. 重用样本切片

// ✅ 推荐:重用切片
samples := []metrics.Sample{
    {Name: "/sched/goroutines:goroutines"},
}

for {
    metrics.Read(samples)
    process(samples[0].Value.Uint64())
}

// ❌ 不推荐:每次都创建新切片
for {
    samples := []metrics.Sample{
        {Name: "/sched/goroutines:goroutines"},
    }
    metrics.Read(samples)
    process(samples[0].Value.Uint64())
}

2. 检查 Kind 类型

// ✅ 推荐:检查类型
sample := metrics.Sample{Name: "/gc/heap/allocs:bytes"}
metrics.Read([]metrics.Sample{sample})

switch sample.Value.Kind() {
case metrics.KindUint64:
    value := sample.Value.Uint64()
case metrics.KindFloat64:
    value := sample.Value.Float64()
case metrics.KindFloat64Histogram:
    hist := sample.Value.Float64Histogram()
}

// ❌ 不推荐:不检查类型直接调用
value := sample.Value.Uint64()  // 可能 panic

3. 使用 All() 发现指标

// ✅ 推荐:动态发现
descs := metrics.All()
for _, desc := range descs {
    if strings.Contains(desc.Name, "heap") {
        // 处理堆相关指标
    }
}

4. 并发安全读取

// ✅ 推荐:每个 goroutine 使用独立的切片
go func() {
    samples := []metrics.Sample{{Name: "/sched/goroutines:goroutines"}}
    metrics.Read(samples)
}()

go func() {
    samples := []metrics.Sample{{Name: "/gc/cycles/total:gc-cycles"}}
    metrics.Read(samples)
}()

// ❌ 不推荐:共享底层内存
samples := []metrics.Sample{{Name: "/sched/goroutines:goroutines"}}
go func() {
    metrics.Read(samples)
}()
go func() {
    metrics.Read(samples)  // 数据竞争
}()

与其他包配合

runtime/pprof

package main

import (
    "os"
    "runtime/metrics"
    "runtime/pprof"
)

func main() {
    // 读取内存指标
    samples := []metrics.Sample{
        {Name: "/memory/classes/total:bytes"},
    }
    metrics.Read(samples)
    
    // 创建 CPU profile
    f, _ := os.Create("cpu.prof")
    pprof.StartCPUProfile(f)
    defer pprof.StopCPUProfile()
    
    // 执行代码...
}

runtime/debug

package main

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

func main() {
    // 使用 metrics 包
    samples := []metrics.Sample{
        {Name: "/gc/gogc:percent"},
    }
    metrics.Read(samples)
    gogc := samples[0].Value.Uint64()
    
    // 使用 debug 包
    old := debug.SetGCPercent(50)
    
    fmt.Printf("GOGC: %d -> 50\n", gogc)
    fmt.Printf("Previous setting: %d\n", old)
}

快速参考

函数

函数参数返回值说明
Readm []Sample-读取指标值

类型

类型说明
Description指标描述
Float64Histogramfloat64 直方图
Sample指标样本
Value指标值
ValueKind值类型标签

Value 方法

方法返回值说明
Float64float64返回 float64 值
Float64Histogram*Float64Histogram返回直方图
KindValueKind返回值类型
Uint64uint64返回 uint64 值

ValueKind 常量

常量说明
KindBad0未知类型
KindUint641uint64
KindFloat642float64
KindFloat64Histogram3直方图

主要指标类别

类别说明指标数
/cgo/*CGO 相关1
/cpu/classes/*CPU 时间分类11
/gc/*垃圾回收25
/godebug/*GODEBUG 行为30
/memory/classes/*内存分类13
/sched/*调度器9
/sync/*同步原语1

注意事项

1. 指标可用性

  • 指标集合可能随 Go 版本变化
  • 某些指标可能被弃用或移除
  • 使用 All() 动态发现更兼容

2. 值类型安全

  • 调用 Uint64/Float64 前必须检查 Kind
  • 错误的类型调用会 panic
  • Kind 保证不会改变

3. 并发使用

  • 多个 Read 调用并发安全
  • 但参数不能共享底层内存
  • 重用时注意数据竞争

4. 性能考虑

  • Read 调用有少量开销
  • 避免过于频繁的读取
  • 重用切片提高效率

5. 直方图解读

  • 桶边界单调递增
  • 计数也是单调递增(累积分布)
  • 需要计算差值得到每个桶的实际计数

总结

runtime/metrics 包提供了访问 Go 运行时指标的统一接口。

核心要点

  1. 通过字符串键访问指标
  2. 使用 All() 发现支持的指标
  3. 检查 Value 的 Kind 后再访问值
  4. 重用样本切片提高效率
  5. 注意并发使用时的数据安全

主要用途

  • 运行时性能监控
  • 内存使用分析
  • GC 行为观察
  • 调度器性能分析
  • 调试和故障排查

优势

  • 统一的访问接口
  • 可扩展的指标集合
  • 跨 Go 实现兼容
  • 稳定的 API 保证