Go學(xué)習(xí)記錄之runtime包深入解析
前言:
先前學(xué)習(xí)到goroutine協(xié)程與channel調(diào)節(jié)并發(fā)同步時,有涉及到關(guān)于runtime包管理goroutine運行時的知識點。由于沒有接觸過,并且出于goroutine的重要性(runtime聽著很高大上)的原因,通過多方學(xué)習(xí)并博客記錄。
一、runtime包內(nèi)容學(xué)習(xí)
1、作用:
其作用主要是與程序的運行時環(huán)境進行交互,提供了一系列函數(shù)和變量,用于控制、管理和監(jiān)視程序的執(zhí)行:
① Goroutine和并發(fā)控制:
runtime包提供了一些函數(shù)來管理goroutine,如創(chuàng)建和銷毀goroutine、設(shè)置最大可同時執(zhí)行的CPU數(shù)目等,以及用于并發(fā)控制的函數(shù),如讓出CPU時間片、鎖定和解鎖goroutine到線程等。
package main import ( "fmt" "runtime" "time" ) func worker() { defer fmt.Println("Worker exiting") runtime.Gosched() // 主動讓出 CPU fmt.Println("Worker running") } func main() { go worker() time.Sleep(time.Second) fmt.Println("Number of goroutines:", runtime.NumGoroutine()) }
② 垃圾回收:
Go語言使用自動垃圾回收機制來管理內(nèi)存,runtime包提供了手動觸發(fā)垃圾回收、設(shè)置垃圾回收的百分比、獲取內(nèi)存統(tǒng)計信息等函數(shù),用于對垃圾回收進行調(diào)控和監(jiān)測。
func main() { var m runtime.MemStats runtime.ReadMemStats(&m) fmt.Printf("Allocated memory: %d bytes\n", m.Alloc) runtime.GC() // 手動觸發(fā) GC runtime.ReadMemStats(&m) fmt.Printf("After GC: %d bytes\n", m.Alloc) }
③ 棧和堆操作:
runtime包提供了函數(shù)來獲取當(dāng)前goroutine的堆棧信息、完整的堆棧跟蹤,以及對堆棧進行分析和剖析的相關(guān)函數(shù)。
④ 系統(tǒng)信息和調(diào)試:
runtime包提供了獲取CPU核心數(shù)、CPU性能分析、調(diào)試信息等函數(shù),可以用于獲取關(guān)于系統(tǒng)的一些基本信息和進行程序的調(diào)試。
⑤ 錯誤處理:
runtime包提供了獲取調(diào)用棧信息、根據(jù)PC地址獲取函數(shù)信息等函數(shù),用于錯誤處理和調(diào)試時追蹤錯誤發(fā)生的位置。
func printStack() { var buf [4096]byte n := runtime.Stack(buf[:], false) fmt.Printf("Stack trace:\n%s\n", string(buf[:n])) } func main() { defer printStack() panic("Something went wrong!") }
⑥ P操作:
runtime包提供了與處理器(P)的相關(guān)操作,如獲取可同時執(zhí)行的最大P數(shù)目、綁定goroutine到特定的P等。
⑦ 信號處理:
runtime包提供了處理C語言信號的函數(shù),用于和外部C庫進行交互時的信號處理。
通過使用runtime包提供的函數(shù)和變量,可以更控制程序的運行時行為、優(yōu)化性能、處理錯誤和調(diào)試問題。但需要注意,對runtime包過度依賴可能會降低代碼的可移植性
2、相關(guān)函數(shù):
runtime包提供了與程序運行時環(huán)境交互的函數(shù)和變量,包含了與上述作用相關(guān)的函數(shù):
① Goroutine相關(guān):
goexit():終止當(dāng)前goroutine的執(zhí)行。
GOMAXPROCS(n int):設(shè)置可同時執(zhí)行的最大CPU數(shù)。
NumGoroutine():返回當(dāng)前存在的goroutine數(shù)目。
② 垃圾回收:
GC(): 手動觸發(fā)垃圾回收。
SetFinalizer(obj interface{}, finalizer interface{}):為對象設(shè)置一個垃圾回收的終結(jié)器(finalizer),當(dāng)對象被垃圾回收時,終結(jié)器會被調(diào)用。
③ 棧:
Stack(buf []byte, all bool): 返回當(dāng)前goroutine的堆棧信息。
StackTrace(p *Profiling)::返回當(dāng)前程序的完整堆棧跟蹤。
④ 內(nèi)存統(tǒng)計:
ReadMemStats(m *MemStats): 獲取當(dāng)前程序的內(nèi)存使用統(tǒng)計信息。
⑤ 并發(fā)控制:
LockOSThread(): 鎖定當(dāng)前goroutine到當(dāng)前線程上。
UnlockOSThread(): 解除當(dāng)前goroutine對當(dāng)前線程的鎖定。(等)
同樣的,由于其對底層進行交互,過多使用相關(guān)函數(shù)也會降低可移植性,開發(fā)過程中需要注意。
3、幾個較為重要的函數(shù):
① runtime.Gosched():讓出CPU時間片,重新等待安排任務(wù)
package main import ( "fmt" "runtime" ) func main() { go func(s string) { for i := 0; i < 2; i++ { fmt.Println(s) } }("world") // 主協(xié)程 for i := 0; i < 2; i++ { // 切一下,再次分配任務(wù) runtime.Gosched() fmt.Println("hello") } }
② runtime.Goexit():退出當(dāng)前協(xié)程
package main import ( "fmt" "runtime" ) func main() { go func() { defer fmt.Println("A.defer") func() { defer fmt.Println("B.defer") // 結(jié)束協(xié)程 runtime.Goexit() defer fmt.Println("C.defer") fmt.Println("B") }() fmt.Println("A") }() for { } }
③ runtime.GOMAXPROCS:
Go運行時的調(diào)度器使用GOMAXPROCS參數(shù)來確定需要使用多少個OS線程來同時執(zhí)行Go代碼。默認值是機器上的CPU核心數(shù)。例如在一個8核心的機器上,調(diào)度器會把Go代碼同時調(diào)度到8個OS線程上(GOMAXPROCS是m:n調(diào)度中的n)。
Go語言中可以通過runtime.GOMAXPROCS()函數(shù)設(shè)置當(dāng)前程序并發(fā)時占用的CPU邏輯核心數(shù)。
Go1.5版本之前,默認使用的是單核心執(zhí)行。Go1.5版本之后,默認使用全部的CPU邏輯核心數(shù)。
可以通過將任務(wù)分配到不同的CPU邏輯核心上實現(xiàn)并行的效果
func a() { for i := 1; i < 10; i++ { fmt.Println("A:", i) } } func b() { for i := 1; i < 10; i++ { fmt.Println("B:", i) } } func main() { runtime.GOMAXPROCS(1) go a() go b() time.Sleep(time.Second) }
兩個任務(wù)只有一個邏輯核心,此時是做完一個任務(wù)再做另一個任務(wù)。 將邏輯核心數(shù)設(shè)為2,此時兩個任務(wù)并行執(zhí)行,代碼如下。
func a() { for i := 1; i < 10; i++ { fmt.Println("A:", i) } } func b() { for i := 1; i < 10; i++ { fmt.Println("B:", i) } } func main() { runtime.GOMAXPROCS(2) go a() go b() time.Sleep(time.Second) }
二、底層設(shè)計:
Go語言的高效與簡潔,離不開其運行時環(huán)境。runtime包作為Go語言運行時系統(tǒng)的核心接口,承載著內(nèi)存管理、協(xié)程調(diào)度、垃圾回收、類型信息處理等關(guān)鍵功能。深入理解runtime包的底層設(shè)計,可以深入了解Go語言的運行機制。
1、內(nèi)存管理與垃圾回收的底層協(xié)作
① 內(nèi)存分配策略
runtime包中的內(nèi)存分配器采用分層設(shè)計,分為線程緩存(Thread Cache)、中心緩存(Central Cache)和堆(Heap)三個層級:
• 線程緩存:每個Go線程擁有獨立的線程緩存,用于快速分配小對象(通常小于16字節(jié)),避免頻繁訪問全局資源,減少鎖競爭;
• 中心緩存:作為線程緩存的補充,負責(zé)從堆中獲取大塊內(nèi)存,并切割成適合線程緩存的小塊,平衡各線程間的內(nèi)存使用;
• 堆:內(nèi)存分配的最終來源,由垃圾回收器統(tǒng)一管理,當(dāng)線程緩存和中心緩存無法滿足需求時,直接從堆中分配內(nèi)存 。
② 垃圾回收協(xié)同工作
垃圾回收器(GC)與內(nèi)存分配器緊密配合,runtime包通過一系列函數(shù)控制GC的行為,如runtime.GC()可手動觸發(fā)垃圾回收。在GC過程中:
• 標記階段:從根對象(如全局變量、棧上變量)出發(fā),標記所有可達對象;
• 清除階段:回收未被標記的對象,將其占用的內(nèi)存歸還到空閑列表;
• 整理階段(可選):在特定情況下,對堆內(nèi)存進行整理,減少內(nèi)存碎片 。
2、協(xié)程調(diào)度的底層實現(xiàn)細節(jié)
runtime包是協(xié)程調(diào)度的核心執(zhí)行者,基于M:N調(diào)度模型實現(xiàn)高效的協(xié)程調(diào)度:
• Goroutine(G):用戶態(tài)的輕量級線程,是Go語言并發(fā)的基本單元;
• Machine(M):對應(yīng)操作系統(tǒng)線程,負責(zé)執(zhí)行Goroutine中的代碼;
• Processor(P):協(xié)程調(diào)度的關(guān)鍵組件,維護本地協(xié)程隊列,綁定到M上以減少上下文切換開銷。
調(diào)度過程中,runtime包通過以下機制確保協(xié)程高效運行:
• 本地隊列優(yōu)先:P優(yōu)先從本地隊列獲取Goroutine執(zhí)行,減少競爭;
• 工作竊取算法:當(dāng)本地隊列為空時,P從其他P的本地隊列竊取Goroutine,均衡負載;
• 阻塞處理:當(dāng)M執(zhí)行的Goroutine發(fā)生阻塞(如I/O操作),M與P分離,P綁定到其他空閑M繼續(xù)執(zhí)行其他Goroutine,避免線程浪費 。
3、運行時類型信息與反射支持
runtime包管理著程序中所有類型的元數(shù)據(jù),這些信息是反射功能的基礎(chǔ):
• 類型描述:通過結(jié)構(gòu)體記錄類型的大小、對齊方式、方法集合等信息,如rtype結(jié)構(gòu)體存儲類型的基本屬性,itab結(jié)構(gòu)體記錄接口類型與具體實現(xiàn)類型的映射關(guān)系;
• 反射實現(xiàn):reflect包依賴runtime包提供的類型信息,在運行時動態(tài)獲取對象的類型、調(diào)用方法、修改屬性 。
例如,在進行反射操作時,runtime包負責(zé)提供類型比較、方法查找等底層支持,確保反射操作的正確性和高效性。
4、runtime包與開發(fā)者實踐
雖然runtime包的大部分功能在幕后自動運行,但開發(fā)者在某些場景下仍需關(guān)注其行為:
• 性能調(diào)優(yōu):通過runtime.GOMAXPROCS()設(shè)置處理器數(shù)量,優(yōu)化協(xié)程調(diào)度效率;利用runtime/pprof包進行性能分析,定位內(nèi)存泄漏、CPU瓶頸等問題;
• 特殊場景處理:在需要與操作系統(tǒng)深度交互時,使用runtime包提供的系統(tǒng)調(diào)用接口;在編寫高性能程序時,通過runtime包的內(nèi)存分配函數(shù)手動管理內(nèi)存 。
總結(jié)
到此這篇關(guān)于Go學(xué)習(xí)記錄之runtime包深入解析的文章就介紹到這了,更多相關(guān)Go runtime包內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于golang的簡單分布式延時隊列服務(wù)的實現(xiàn)
這篇文章主要介紹了基于golang的簡單分布式延時隊列服務(wù)的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02Go 1.21新增的slices包中切片函數(shù)用法詳解
Go 1.21新增的 slices 包提供了很多和切片相關(guān)的函數(shù),可以用于任何類型的切片,本文通過代碼示例為大家介紹了部分切片函數(shù)的具體用法,感興趣的小伙伴可以了解一下2023-08-08Golang基于epoll實現(xiàn)最簡單網(wǎng)絡(luò)通信框架
這篇文章主要為大家詳細介紹了Golang如何基于epoll實現(xiàn)最簡單網(wǎng)絡(luò)通信框架,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)學(xué)習(xí)2023-06-06golang?cache帶索引超時緩存庫實戰(zhàn)示例
這篇文章主要為大家介紹了golang?cache帶索引超時緩存庫實戰(zhàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-09-09