Go学习十六:select和GC

430 阅读5分钟

这是我参与8月更文挑战的第27天,活动详情查看:8月更文挑战

select简介

  • Golang中select和switch结构特别像,但是select中case的条件只能是I/O
  • select 的语法(condition是条件)
select{
  case condition:
  case condition:
  default:
}
  • select执行过程:
    • 每个case必须是一个IO操作
    • 哪个case可以执行就执行哪个
    • 多个case都可以执行,随机执行一个
    • 所有case都不能执行时,执行default
    • 所有case都不能执行,且没有default,将会阻塞
  • 代码示例
func main() {
   runtime.GOMAXPROCS(1)
   ch1 := make(chan int, 1)
   ch2 := make(chan string, 1)
   ch1 <- 1
   ch2 <- "hello"
   select {
   case value := <-ch1:
      fmt.Println(value)
   case value := <-ch2:
      fmt.Println(value)
   }
}
  • select多和for循环结合使用,下面例子演示出了一直在接收消息的例子
package main

import (
	"fmt"
)

func main() {
	ch := make(chan int)
	for i := 1; i <= 5; i++ {
		go func(arg int) {
			ch <- arg
		}(i)
	}
  //如果是一直接受消息,应该是死循环for{},下面代码中是明确知道消息个数
	for i := 1; i <= 5; i++ {
		select {
		case c := <-ch:
			fmt.Println("取出数据", c)
		default:
			//没有default会出现死锁
		}
	}
	fmt.Println("程序执行结束")
}

  • break可以对select生效,如果for中嵌套select,break选择最近结构

GC

  • GC英文全称 garbage collector
  • Go语言GC是相对C/C++语言非常重要的改进
  • 一些常用GC算法
    • 引用计算法.当对象被引用时计算器加一.不被引用计数器减一
      • PHP和Object-C使用
      • 相互引用无法回收
      • 计数增加消耗
    • Mark And Sweep 标记和清除算法.停止程序运行,递归遍历对象,进行标记.标记完成后将所有没有引用的对象进行清除
      • 由于标记需要停止程序(Stop the world),当对象特别多时,标记和清除过程比较耗时(可能几百毫秒),很难接受
    • 三色标记法:是Mark And Sweep的改进版.从逻辑上分为白色区(未搜索),灰色区(正搜索),黑色区(已搜索).灰色区内容是子引用没有进行搜索,黑色区表示子引用存在
    • 分代收集.一般情况都有三代,例如java中新生代,老年代,永久代.当新生代中带有阈值时会把对象放入到老年代,相同道理老年代内容达到阈值会放入到永久代

Go语言中的GC

  • Go语言中采用Stop The World方式
  • Golang每个版本基本上都会对GC进行优化,从Golang1.5开始支持并发(concurrent )收集,从1.8版本已经把STW时间优化到了100微妙,通常只需要10微妙以下.且在1.10版本时再次优化减少GC对CPU占用
  • Go语言中GC是自动运行的,在下列情况下会触发GC
    • 当需要申请内存时,发现GC是上次GC两倍时会触发
    • 每2分钟自动运行一次GC
  • GC调优
    • 小对象复用,局部变量尽量少声明,多个小对象可以放入到结构体,方便GC扫描
    • 少用string的”+”
  • 在runtime包下mgc.go中明确的说明了Golang的GC的解释
// Garbage collector (GC).
//
// 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.
//
// The algorithm decomposes into several steps.
// This is a high level description of the algorithm being used. For an overview of GC a good
// place to start is Richard Jones' gchandbook.org.
//
// The algorithm's intellectual heritage includes Dijkstra's on-the-fly algorithm, see
// Edsger W. Dijkstra, Leslie Lamport, A. J. Martin, C. S. Scholten, and E. F. M. Steffens. 1978.
// On-the-fly garbage collection: an exercise in cooperation. Commun. ACM 21, 11 (November 1978),
// 966-975.
// For journal quality proofs that these steps are complete, correct, and terminate see
// Hudson, R., and Moss, J.E.B. Copying Garbage Collection without stopping the world.
// Concurrency and Computation: Practice and Experience 15(3-5), 2003.
//
// 1. GC performs sweep termination.
//
//    a. Stop the world. This causes all Ps to reach a GC safe-point.
//
//    b. Sweep any unswept spans. There will only be unswept spans if
//    this GC cycle was forced before the expected time.
//
// 2. GC performs the "mark 1" sub-phase. In this sub-phase, Ps are
// allowed to locally cache parts of the work queue.
//
//    a. Prepare for the mark phase by setting gcphase to _GCmark
//    (from _GCoff), enabling the write barrier, enabling mutator
//    assists, and enqueueing root mark jobs. No objects may be
//    scanned until all Ps have enabled the write barrier, which is
//    accomplished using STW.
//
//    b. Start the world. From this point, GC work is done by mark
//    workers started by the scheduler and by assists performed as
//    part of allocation. The write barrier shades both the
//    overwritten pointer and the new pointer value for any pointer
//    writes (see mbarrier.go for details). Newly allocated objects
//    are immediately marked black.
//
//    c. GC performs root marking jobs. This includes scanning all
//    stacks, shading all globals, and shading any heap pointers in
//    off-heap runtime data structures. Scanning a stack stops a
//    goroutine, shades any pointers found on its stack, and then
//    resumes the goroutine.
//
//    d. GC drains the work queue of grey objects, scanning each grey
//    object to black and shading all pointers found in the object
//    (which in turn may add those pointers to the work queue).
//
// 3. Once the global work queue is empty (but local work queue caches
// may still contain work), GC performs the "mark 2" sub-phase.
//
//    a. GC stops all workers, disables local work queue caches,
//    flushes each P's local work queue cache to the global work queue
//    cache, and reenables workers.
//
//    b. GC again drains the work queue, as in 2d above.
//
// 4. Once the work queue is empty, GC performs mark termination.
//
//    a. Stop the world.
//
//    b. Set gcphase to _GCmarktermination, and disable workers and
//    assists.
//
//    c. Drain any remaining work from the work queue (typically there
//    will be none).
//
//    d. Perform other housekeeping like flushing mcaches.
//
// 5. GC performs the sweep phase.
//
//    a. Prepare for the sweep phase by setting gcphase to _GCoff,
//    setting up sweep state and disabling the write barrier.
//
//    b. Start the world. From this point on, newly allocated objects
//    are white, and allocating sweeps spans before use if necessary.
//
//    c. GC does concurrent sweeping in the background and in response
//    to allocation. See description below.
//
// 6. When sufficient allocation has taken place, replay the sequence
// starting with 1 above. See discussion of GC rate below.