The GC runs concurrently with mutator threads, is type accurate (aka precise), allows multiple GC thread to run in parallel. It is a concurrent mark and sweep that uses a write barrier. It is non-generational and non-compacting. Allocation is done using size segregated per P allocation areas to minimize fragmentation while eliminating locks in the common case.

    垃圾回收是和go线程同时运行的,它是类型精确的,而且多个垃圾回收线程可以并行运行。它是一种使用了写屏障的并发标记清除的垃圾回收方式。它是非分代和非压缩的。使用按P分配区域隔离的大小来完成分配,以最小化碎片,同时消除常见情况下的锁。

    这里面有很多术语,我一会儿会解释。但是首先,我会为你展示一种查看垃圾回收过程的参数的方式。幸运的是,Go标准库提供了方法,允许你去学习垃圾回收器的操作以及了解更多关于垃圾回收器在背后所做的事情。相关的代码保存在了gColl.go中,它有三个部分。

    注意到每次你都需要获取更多最近的垃圾回收统计信息,你会需要调用方法。printStats()方法的目的是去避免每次要写相同代码。

    第二部分代码如下:

    1. func main() {
    2. f, err := os.Create("/tmp/traceFile.out")
    3. if err != nil {
    4. panic(err)
    5. }
    6. defer f.Close()
    7. err = trace.Start(f)
    8. if err != nil {
    9. fmt.Println(err)
    10. return
    11. }
    12. defer trace.Stop()
    13. printStats(mem)
    14. for i := 0; i < 10; i++ {
    15. s := make([]byte, 50000000)
    16. if s == nil {
    17. fmt.Println("Operation failed!")
    18. }
    19. }
    20. printStats(mem)

    for循环里创建一堆大的go slices,目的是为了进行大量内存分配来触发垃圾回收。

    在macOS High Sierra上面gColl.go的输出如下:

    1. mem.Alloc: 66024
    2. mem.TotalAlloc: 66024
    3. mem.HeapAlloc: 66024
    4. -----
    5. mem.TotalAlloc: 500117056
    6. mem.HeapAlloc: 50078496
    7. mem.NumGC: 10
    8. -----
    9. mem.Alloc: 76712
    10. mem.TotalAlloc: 1500199904
    11. mem.HeapAlloc: 76712
    12. mem.NumGC: 20
    13. -----

    尽管你不会每次都去检查go垃圾收集器的操作,但是在一个慢的应用程序上可以看到go垃圾回收器的工作方式,长时间的运行里会节省你很多时间。我很确定,花点时间去整体学习了解垃圾回收,尤其是了解go垃圾回收器的工作方式,你不会后悔的。

    这里有一个技巧可以让你得到更多关于go 垃圾收集器操作的细节,使用下面这个命令。

    1. gc 4 @0.025s 0%: 0.002+0.65+0.018 ms clock, 0.021+0.040/0.057/0.003+0.14 ms cpu, 47->47->0 MB, 48 MB goal, 8 P

    这些数据给你提供了更多垃圾回收过程中的堆内存大小的信息。让我们以47->47->0 MB这三个值为例。第一个数值是垃圾回收器要去运行时候的堆内存大小。第二个值是垃圾回收器操作结束时候的堆内存大小。最后一个值就是生存堆的大小。