1、值的高效处理和存储,允许创建紧凑的数据结构,避免不必要的填充字节。紧凑的数据结构能更好地利用缓存。更好的缓存利用率可带来更好的性能。
2、函数的调用有开销,减少函数调用开销的解决方案是内联。简单的函数可以被 Go 编译器内联。
3、强制垃圾回收使 Go 成为一种更简单,更安全的语言。这意味着在堆上分配的内存是有代价的。每次 GC 运行时都会花费 CPU 时间,直到释放内存为止。逃逸分析的重要性,增加变量在栈分配,减少在堆分配。
4、进程切换的开销很大,所以出现了线程,共享相同的内存空间。由于线程共享地址空间,因此它们比进程更轻,因此创建速度更快,切换速度更快。Goroutine 升华了线程。
Goroutine 可能会给禅让给其他协程时刻是:
- 阻塞式通道发送和接收。
- Go 声明,虽然不能保证会立即调度新的 goroutine。
- 文件和网络操作式的阻塞式系统调用。
- 在被垃圾回收循环停止后。
每一个 Go 进程的操作系统线程相对较少,Go 运行时负责将可运行的 Goroutine 分配给空闲的操作系统线程。
5、进程地址空间有堆、栈来保存变量,堆自下而上,栈自上而下,防止相互穿透,有保护页,称为不可写内存区域。每个线程都会分配自己的栈,随着程序中线程数的增加,可用地址空间的数量会减少。Go 编译器不使用保护页,而是在每个函数调用时插入一个检查,以检查是否有足够的栈来运行该函数。如果没有,运行时可以分配更多的栈空间。为了解决热分裂问题,Go 1.3 采用了一种新的栈管理方法。如果 goroutine 的栈太小,则不会添加和删除其他栈段,而是分配新的更大的栈。旧栈的内容被复制到新栈,然后 goroutine 使用新的更大的栈继续运行。
总结:这五个特性一样强大,它们不是孤立存在的。例如,运行时将 goroutine 复用到线程上的方式在没有可扩展栈的情况下几乎没有效率。内联通过将较小的函数组合成较大的函数来降低栈大小检查的成本。逃逸分析通过自动将从实例从堆移动到栈来减少垃圾回收器的压力。逃逸分析还提供了更好的缓存局部性Cache Locality。如果没有可增长的栈,逃逸分析可能会对栈施加太大的压力。
you are the best!