Go语言入门学习整理 (四)| 青训营笔记

139 阅读7分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 4 天

一、重点内容:

1.1 知识要点有哪些?

  • Go自动内存管理
  • Go内存管理优化
  • Go编译器优化

二、详细知识点介绍:

2.1 Go自动内存管理

Go语言自动内存管理是Go语言在内存管理方面的一种高效方法,它可以自动分配和释放内存,不必手动处理内存空间的申请与释放,从而有效降低编程复杂度。Go语言的自动内存管理是基于“垃圾回收器”这一技术,它能够自动识别出那些不再使用的对象,从而释放其占用的内存空间,确保程序性能不受影响。

Go语言的垃圾回收器是一个自动管理内存的系统,它使用称为“标记-清除”的技术来释放不再需要的内存空间。 Go语言的垃圾回收器和C/C++中的malloc()、free()有所不同:它不需要显式地调用malloc()来分配内存空间,也不需要显式地调用free()来释放内存空间,它可以自动完成这一过程。 Go语言垃圾回收器使用逃逸分析等技术来避免垃圾回收器的性能下降。它也使用一些技术,如并行和并发机制,以保证独立的Go程序的效率和稳定性。此外,Go语言还内置了“析构函数”,它可以在对象释放前做一些清理工作,比如释放文件资源和数据库连接。

2.2 Go内存管理优化

Go 语言的内存管理是由 Go 语言自身提供的,它具有非常高效、灵活和可扩展的特点。Go 的内存管理有两个重要功能:分配内存和回收内存。Go 为了更高效的进行内存管理,借助于一种叫做“逃逸分析”的技术。逃逸分析会分析每一个函数,以确定变量是在函数内部使用,还是外部使用(将变量传递给其他函数),从而决定该变量应当如何分配内存,以及是否需要进行回收。

  • 分块: Go内存管理采用分块(Block)的方式管理内存。Go程序使用栈来存放局部变量,每一个Goroutine都有一个独立的栈。Go为每个Goroutine在创建时分配一个固定大小的分块(Block),这个分块的大小固定,不会随着Goroutine的使用而改变。Go中的内存分配是以块为单位的,而不是以字节为单位的。当Goroutine 要使用更多的内存时,会申请更大的块,然后将原来的块的内存拷贝到新的块中,原来的块就归还给系统。这样,就不会出现内存耗尽的情况了。
  • 缓存: Go的内存管理还提供了一种叫做缓存的机制,它可以帮助Goroutine在多次使用同一块内存时,减少内存拷贝的开销。缓存能够把数据存储到不同类型的缓存中,比如,将一个核心中的数据存储到每个线程中的缓存中,或者将数据存储到同一线程中的多个缓存中,从而可以加快Goroutine对数据的访问速度。

2.2.1 Balanced GC

Balanced GC(平衡垃圾收集)是Go语言中的一种垃圾收集机制,它能够降低内存管理开销,提高程序性能。Balanced GC通过把程序内存划分为几个大小相等的内存栈,当每一个内存栈的使用空间达到一定限度的时候,就会进行垃圾回收。这种方式可以确保使用内存的平衡,避免因数据不均衡而导致的内存占用过大和数据过多等问题,同时又能避免由于垃圾回收频繁而导致的性能问题。另外,Balanced GC还支持并行垃圾收集,可以有效减少内存管理的开销,提升垃圾收集的性能。当然,Go语言还支持传统的Stop-the-world式垃圾收集方式,但性能没有Balanced GC好。

2.3 静态分析

Go编译器使用静态代码分析,即在编译阶段根据源代码对程序进行分析。Go编译器可以对程序的语法、语义等进行分析,并且会对源码中存在的错误进行检查,把潜在的问题和不良的编码习惯暴露出来,从而保证编写的代码更加安全可靠。

  1. 语法分析:根据源代码生成抽象语法树(AST),AST会以可视化的形式呈现出来,有助于理解Go程序的结构。
  2. 语义分析:用来检测源码中的语法错误、变量名称冲突、变量类型不匹配和其他一些编码问题。
  3. 代码优化:用来确定源码中存在的性能问题,比如不必要的循环嵌套,多余的变量声明等。
  4. 生成目标文件:最后,Go编译器会根据源码生成可执行文件,可以直接在目标系统上运行。

数据分析的目的是让编译器能够分析出变量的类型,优化变量的使用,并检测变量的冲突和错误使用。 控制流分析就是在编译过程中,根据源代码来分析程序对变量的调用和处理,以便最终生成正确的可执行文件。

过程内分析指的是对特定程序代码进行静态分析,以识别可能存在的BUG、漏洞或其他不安全因素。
过程间分析指的是将多个应用程序进行交叉分析,发现各自的不足及其影响。

2.4 编译器优化

  • 函数内联(Inlining)是一种在编译时将代码中的函数体插入到函数调用处的优化方法,它可以大幅度减少函数调用次数,从而提升程序的性能。Go语言使用了“Pragma inliner”优化器,可以根据内联函数的特征、函数体大小、代码复杂度等因素,选择合适的函数进行内联优化。

  • Beast Mode是Go语言特有的一种性能优化技术,它可以动态地根据应用程序的需要对程序进行优化,包括机器指令的重新排序、内存布局的调整等。Beast Mode通过改进程序内部运行顺序,可以消除不必要的调用开销,使程序更快、更高效地运行。

  • 逃逸分析是一种在Go编译器中针对Go语言进行优化的技术,它可以准确判断函数的作用域,并确定哪些数据和变量能够逃逸(即在函数调用结束之后仍然存在),从而为Go语言程序提供性能上的改进。此外,Go语言的逃逸分析还可以帮助程序员正确使用变量的作用域,从而保证程序的正确性和安全性。

  • 最后通过micro-benchmark快速验证性能优化。micro-benchmark是一种微型的性能测试,通过比较小的测试程序的性能,来确定由大量程序组成的系统的行为。micro-benchmark可以用来比较不同的算法、不同的内存布局等,从而帮助程序员快速找到并消除性能问题的根源。

三、实践练习例子:

内联函数简单示例:

func Multiply(a, b int) int {
	return a * b
}
// 内联函数
inline Multiply(a, b int) int {
	return a * b
}

下面是一个管理Go内存的代码示例:

func main(){
	//申请一块指定大小的内存
	memory := make([]byte, 1024)

	//使用完成后,释放内存
	defer func(){
		if memory != nil {
			runtime.Free(unsafe.Pointer(&memory[0]))
			memory = nil
		}
	}()
}

四、个人总结:

Go语言性能优化包括减少内存占用、避免不必要的拷贝、复用内存、使用合理的数据结构以及利用并行和并发机制等。此外,Go语言还支持内联函数、垃圾回收以及其他特性,对于Go语言的性能优化也是非常有用的。还有很多其他的Go语言性能优化技巧,如优化数据类型的选择、降低函数调用次数、使用内置的性能分析工具等。此外,还可以通过减少冗余代码、优化内存分配以及利用缓存来提高Go语言的性能。

五、引用参考:

后端入门 - Go 语言原理与实践