Go - 基于逃逸分析來提升程序性能
目錄
前言
為什么需要了解逃逸分析?
因為我們想要提升程序性能,通過逃逸分析我們能夠知道變量是分配到堆上還是棧上,如果分配到棧上,內存的分配和釋放都是由編譯器進行管理,分配和釋放的速度非常快,如果分配到堆上,堆不像棧那樣可以自動清理,它會引起頻繁地進行垃圾回收(GC
),而垃圾回收會占用比較大的系統開銷。
什么是逃逸分析?
在編譯程序優化理論中,逃逸分析是一種確定指針動態范圍的方法,簡單來說就是分析在程序的哪些地方可以訪問到該指針。
簡單的說,它是在對變量放到堆上還是棧上進行分析,該分析在編譯階段完成。如果一個變量超過了函數調用的生命周期,也就是這個變量在函數外部存在引用,編譯器會把這個變量分配到堆上,這時我們就說這個變量發生逃逸了。
如何確定是否逃逸?
go run -gcflags '-m -l' main.go
可能出現逃逸的場景
01
package main
type Student struct {
Name interface{}
}
func main() {
stu := new(Student)
stu.Name = "tom"
}
分析結果:
go run -gcflags '-m -l' 01.go
# command-line-arguments
./01.go:8:12: new(Student) does not escape
./01.go:9:11: "tom" escapes to heap
interface{}
賦值,會發生逃逸,優化方案是將類型設置為固定類型,例如:string
package main
type Student struct {
Name string
}
func main() {
stu := new(Student)
stu.Name = "tom"
}
分析結果:
go run -gcflags '-m -l' 01.go
# command-line-arguments
./01.go:8:12: new(Student) does not escape
02
package main
type Student struct {
Name string
}
func GetStudent() *Student {
stu := new(Student)
stu.Name = "tom"
return stu
}
func main() {
GetStudent()
}
分析結果:
go run -gcflags '-m -l' 02.go
# command-line-arguments
./02.go:8:12: new(Student) escapes to heap
返回指針類型,會發生逃逸,優化方案視情況而定。
函數傳遞指針和傳值哪個效率高嗎?我們知道傳遞指針可以減少底層值的拷貝,可以提高效率,但是如果拷貝的數據量小,由于指針傳遞會產生逃逸,可能會使用堆,也可能會增加 GC
的負擔,所以傳遞指針不一定是高效的。
不要盲目使用變量指針作為參數,雖然減少了復制,但變量逃逸的開銷可能更大。
03
package main
func main() {
nums := make([]int, 10000, 10000)
for i := range nums {
nums[i] = i
}
}
分析結果:
go run -gcflags '-m -l' 03.go
# command-line-arguments
./03.go:4:14: make([]int, 10000, 10000) escapes to heap
棧空間不足,會發生逃逸,優化方案盡量設置容量,如果容量實在過大那就沒辦法了。
小結
- 逃逸分析是編譯器在靜態編譯時完成的。
- 逃逸分析后可以確定哪些變量可以分配在棧上,棧的性能好。
以上,希望對你能夠有所幫助。
推薦閱讀
- 上一篇 ?Java集合--有序性、排序性、穩定性
- 下一篇 ?Java即時編譯和逃逸分析