網站首頁 編程語言 正文
場景
go 如果頻繁地創建、銷毀對象(比如 http 服務的 json 對象,日志內容等),會對 GC 造成壓力。比如下面的 Log
函數,在高并發情況下,需要頻繁地創建和銷毀 buffer。
func Log(w io.Writer, key, val string) { b := new(bytes.Buffer) // 按一定的格式打印日志,這一段不是重點 b.WriteString(time.Now().UTC().Format(time.RFC3339)) b.WriteByte(' ') b.WriteString(key) b.WriteByte('=') b.WriteString(val) b.WriteByte('\n') w.Write(b.Bytes()) }
這時候可以考慮復用這些 buffer。我們可以維護一個 buffer 的對象池,需要的時候從對象池拿 buffer,用完放回對象池。這時候就推薦使用 sync.Pool 了。
sync.Pool 維護著一組對象池,需要時從對象池拿對象,不需要放回對象池就可以了。它有這些特點:
- 忙時會自動擴容對象池,閑時會自動縮容;
- 線程安全;
- 對象池的對象,會未經通知地自動刪除;
- 不能被 copy。
用法
創建
初始化時指定 New 方法。sync.Pool 會通過 New 方法創建對象池的對象。一般返回一個指針。
// 從對象池里取 buffer 時,如果池里沒 buffer了,則調用 New 創建一個新的。 var bufPool = sync.Pool{ New: func() interface{} { return new(bytes.Buffer) }, }
GET & PUT
通過 Get 獲取對象池的對象。當使用完畢,通過 Put 把對象返回對象池。
b := bufPool.Get().(*bytes.Buffer) // 從對象池拿 buffer 對象 // 操作對象,這個不重要 b.Reset() b.WriteString(time.Now().UTC().Format(time.RFC3339)) // 操作完放回對象池 bufPool.Put(b)
優化 Log 函數
Log 函數可以使用 sync.Pool 的優化,代碼如下:
var bufPool = sync.Pool{ New: func() interface{} { return new(bytes.Buffer) }, } func LogWithPool(w io.Writer, key, val string) { // 從對象池拿 buffer b := bufPool.Get().(*bytes.Buffer) b.Reset() // 按一定的格式打印日志,這一段不是重點 b.WriteString(time.Now().UTC().Format(time.RFC3339)) b.WriteByte(' ') b.WriteString(key) b.WriteByte('=') b.WriteString(val) b.WriteByte('\n') w.Write(b.Bytes()) // 放回對象池 bufPool.Put(b) }
性能測試
我們對兩個函數進行性能測試
// 不使用 sync.Pool func BenchmarkLog(b *testing.B) { writer := os.NewFile(0, os.DevNull) for n := 0; n < b.N; n++ { Log(writer, "path", "/search?a=flowers") } } // 使用 sync.Pool 復用 func BenchmarkLogWithPool(b *testing.B) { writer := os.NewFile(0, os.DevNull) for n := 0; n < b.N; n++ { LogWithPool(writer, "path", "/search?a=flowers") } }
結果:
> go test -bench . -benchmem
goos: darwin
goarch: amd64
pkg: example/pool
cpu: Intel(R) Core(TM) i5-1038NG7 CPU @ 2.00GHz
BenchmarkLog-8 ? ? ? ? ? ? ? ? ? 1849365 ? ? ? ? ? ? ? 635.0 ns/op ? ? ? ? ? 112 B/op ? ? ? ? ?2 allocs/op
BenchmarkLogWithPool-8 ? ? ? ? ? 1993304 ? ? ? ? ? ? ? 614.4 ns/op ? ? ? ? ? ?48 B/op ? ? ? ? ?1 allocs/op
PASS
ok ? ? ?example/pool ? ?4.333s
相比之下,使用 Sync.Pool 和不使用的時候,內存的使用比為 48:112,優化效果還是挺明顯的。
參考:
[1]. pkg.go.dev/sync#Pool
原文鏈接:https://juejin.cn/post/7166051649335590925
相關推薦
- 2022-06-04 R語言批量讀取某路徑下文件內容的方法_R語言
- 2022-08-20 Python操作HDF5文件示例_python
- 2023-04-06 C語言如何實現循環輸入_C 語言
- 2022-09-16 Go1.16新特性embed打包靜態資源文件實現_Golang
- 2022-08-25 C語言詳細分析宏定義與預處理命令的應用_C 語言
- 2023-07-22 spark啟動參數性能優化
- 2022-08-19 SpringMVC異常處理器
- 2022-10-18 go日志庫中的logrus_Golang
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支