Golang 逃逸分析
堆内存与栈内存 Go 在 2 个位置为变量分配内存,全局堆(heap)空间,和每个 goroutine 的栈(stack)空间。 Go 语言实现 GC 机制,因此开发者不需要关心内存分配在栈上,还是堆上。但是从性能的角度出发,在栈上分配内存和在堆上分配内存,性能差异是非常大的。 ...
堆内存与栈内存 Go 在 2 个位置为变量分配内存,全局堆(heap)空间,和每个 goroutine 的栈(stack)空间。 Go 语言实现 GC 机制,因此开发者不需要关心内存分配在栈上,还是堆上。但是从性能的角度出发,在栈上分配内存和在堆上分配内存,性能差异是非常大的。 ...
在 Go 语言中,空结构体 struct{} 是一个非常特殊的类型,它不包含任何字段且不占用任何内存空间。 空结构体不占用内存空间 1type Empty struct{} 2 3func main() { 4 5 var s1 struct{} 6 s2 := Empty{} 7 s3 := struct{}{} 8 9 fmt.Printf("s1 addr: %p, size: %d\n", &s1, unsafe.Sizeof(s1)) 10 fmt.Printf("s2 addr: %p, size: %d\n", &s2, unsafe.Sizeof(s2)) 11 fmt.Printf("s3 addr: %p, size: %d\n", &s3, unsafe.Sizeof(s3)) 12 fmt.Printf("s1 == s2 == s3: %t\n", s1 == s2 && s2 == s3) 13} 得到输出如下 ...
1. WaitGroup WaitGroup 等待一组 Goroutine 完成。主 Goroutine 调用 Add(delta) 来设置要等待的 Goroutine 的数量。然后每个 Goroutine 运行并在完成时调用 Done()。同时,可以使用 Wait() 来阻塞,直到所有 Goroutine 完成。 1type WaitGroup struct { 2 noCopy noCopy 3 4 // 64-bit value: high 32 bits are counter, low 32 bits are waiter count. 5 // 64-bit atomic operations require 64-bit alignment, but 32-bit 6 // compilers do not ensure it. So we allocate 12 bytes and then use 7 // the aligned 8 bytes in them as state, and the other 4 as storage 8 // for the sema. 9 state1 [3]uint32 10} wg.Add(delta int):Add 将 delta(可能为负)添加到 WaitGroup 计数器。如果计数器变为 0,所有在 Wait 时阻塞的 Goroutine 将被释放。如果计数器变成负值,Add 会 panic。 wg.Done():当 WaitGroup 同步等待组中的某个 Goroutine 执行完毕后,设置这个 WaitGroup 的 counter 数值减 1,其实就是调用了 Add(-1)。 wg.Wait():表示让当前的 Goroutine 等待,进入阻塞状态。一直到 WaitGroup 的计数器为 0,才能解除阻塞,这个 Goroutine 才能继续执行。 总之,WaitGroup 让某个协程等待其它若干协程都先完成它们各自的任务。 ...
1. 基础特性 1.1 先进后出 对于多个defer语句,类似栈,按先进后出的顺序执行。 1func main() { 2 var whatever [5]struct{} 3 for i := range whatever { 4 defer fmt.Print(i) 5 } 6} 7 8// 43210 1.2 实时解析函数参数 1package main 2 3import "fmt" 4 5func test(a int) {//无返回值函数 6 defer fmt.Println("1、a =", a) //方法 7 defer func(v int) { fmt.Println("2、a =", v)} (a) //有参函数 8 defer func() { fmt.Println("3、a =", a)} () //无参函数 9 a++ 10} 11func main() { 12 test(1) 13} 14 15// 3、a = 2 16// 2、a = 1 17// 1、a = 1 1.3 return 返回机制 defer、return、返回值三者的执行逻辑应该是: ...
使用golang标准库中的html/template时,在默认情况下渲染模版时为了安全等原因,会将字符串中的部分符号进行转义。 注册自定义转义处理函数 1func unescapeHTML(s string) template.HTML { 2 return template.HTML(s) 3} 在定义转义处理函数后,我们需要使用Funcs()将其注册到模版中。需要注意,注册自定义函数需要在调用Parse()前进行。在注册时我们需要定义一个函数标识符,并在模版文本中使用。在下面例子中我们使用了名为unescapeHTML的函数标识符。 ...
并发过高导致程序崩溃 1func main() { 2 var wg sync.WaitGroup 3 for i := 0; i < math.MaxInt32; i++ { 4 wg.Add(1) 5 go func(i int) { 6 defer wg.Done() 7 fmt.Println(i) 8 time.Sleep(time.Second) 9 }(i) 10 } 11 wg.Wait() 12} 1goroutine 1489841 [running]: 2internal/poll.(*fdMutex).rwlock(0xc000130060, 0x0?) 3 /usr/local/go/src/internal/poll/fd_mutex.go:147 +0x11b 4internal/poll.(*FD).writeLock(...) 5 /usr/local/go/src/internal/poll/fd_mutex.go:239 6internal/poll.(*FD).Write(0xc000130060, {0xc0ca328a90, 0x8, 0x8}) 7 /usr/local/go/src/internal/poll/fd_unix.go:370 +0x72 8os.(*File).write(...) 9 /usr/local/go/src/os/file_posix.go:48 10os.(*File).Write(0xc00012e008, {0xc0ca328a90?, 0x8, 0xc0cd90b750?}) 11 /usr/local/go/src/os/file.go:175 +0x65 12fmt.Fprintln({0x4b7ff8, 0xc00012e008}, {0xc0cd90b790, 0x1, 0x1}) 13 /usr/local/go/src/fmt/print.go:305 +0x75 14fmt.Println(...) 15 /usr/local/go/src/fmt/print.go:314 16main.main.func1(0x0?) 17 /root/zhuangqf/Blank/cmd/main.go:16 +0x8f 18created by main.main 19 /root/zhuangqf/Blank/cmd/main.go:14 +0x3c 20panic: too many concurrent operations on a single file or socket (max 1048575) 1panic: too many concurrent operations on a single file or socket (max 1048575) 1 个 file/socket 的上并发操作个数超过了上限(1048575),简而言之,系统的资源被耗尽了。0xFFFFF = 1048575 ...
1. future/promise 1.1 单向接收 Channel 作为函数返回 sumSquares函数调用的两个实参请求并发进行。每个通道读取操作将阻塞到请求返回结果为止。 1func longTimeRequest() <-chan int64 { 2 r := make(chan int64) 3 4 go func() { 5 time.Sleep(time.Second * 3) // 模拟一个工作负载 6 r <- rand.Int63n(100) 7 }() 8 9 return r 10} 11 12func sumSquares(a, b int64) int64 { 13 return a*a + b*b 14} 15 16func main() { 17 18 a, b := longTimeRequest(), longTimeRequest() 19 fmt.Println(sumSquares(<-a, <-b)) 20} 1.2 单向发送 Channel 作为函数实参 sumSquares函数调用的两个实参的请求也是并发进行的。和上例不同的是longTimeRequest函数接收一个单向发送通道类型参数而不是返回一个单向接收通道结果。 ...
参考资料: https://go.dev/wiki/CodeReviewComments https://go.dev/wiki/CommonMistakes Review Comments 使用 gofmt 或者 goimports 自动格式化代码 记录声明的注释应该是完整的句子,注释应以所描述事物的名称开头并以句点结束。 1// Request represents a request to run a command. 2type Request struct { ... 3 4// Encode writes the JSON encoding of req to w. 5func Encode(w io.Writer, req *Request) { ... context.Context 类型的值携带跨 API 和进程边界的安全凭证、跟踪信息、截止日期和取消信号。Go 程序沿着从传入 RPC 和 HTTP 请求到传出请求的整个函数调用链显式传递上下文。大多数使用 Context 的函数都将它作为第一个参数: 1func F(ctx context.Context, /* other arguments */) {} 注意,不要将 Context 成员添加到结构类型中;而是向需要传递该类型的每个方法添加一个 ctx 参数。 ...
1. panic 与 error 1.1 panic panic 会中止程序执行进入异常处理逻辑,panic可以在当前函数或者调用链向上的任何一层被defer recover 捕获处理,没被捕获的panic会使程序打印堆栈后异常退出。 ...