sync.poolに移動

sync.Poolのドキュメントの無料改定

ガベージコレクタ(以降GC)は、ガベージを常に収集するのではなく、一定の間隔で収集します。 コードが一部のデータ構造にメモリを割り当ててから、それらを解放する場合(円内など)、これにより、新しいメモリを割り当てるためにruntimeをOSに強制するなど、GCに負荷がかかります。 想像してみてください: ピース (たとえば[]byte )を選択し、それを操作し、リリースします。 GCが「スリープから復帰」してこのピースを収集するまでに時間がかかります。 この時点で同じピースをもう1つ割り当て、OSに十分なメモリが既に割り当てられていない場合、アプリケーションはOSにさらにメモリを要求するように強制されます。 アプリケーションの時間に応じて、OSからのメモリ要求は永遠に続きます。 そして、まさにこの時点で、どこかほこりっぽい、その古い「うまくいった」 作品の時間を待っています。
どうする?


プールを作成

 import( "sync" ) var bytesPool = sync.Pool{ New: func() interface{} { return []byte{} }, } /*     `New`  .   ,  `New`  `nil` -        .      `interace{}` -    .   -    . */ 

状態をリセット

 //  ary   []byte     ary = ary[:0] //  len,  cap 

プールに入れる

 /*          ,       (    ) -  ; :   2048        500-800 ,         -        */ const maxCap = 1024 if cap(ary) <= maxCap { //       bytesPool.Put(ary) } 

プールから取る

 nextAry := bytesPool.Get().([]byte) 

Newに関する説明

New関数は、空の[]byte{}作成し、これらの変換もinterface{} 、またはその逆も行います。 []byte場合、 appendを使用してビルドする可能性が最も高いため、基本的にこのアプローチは採算が取れません。


プール全体を扱う2つの関数を作成する方がはるかに便利です。

 //  func getBytes() (b []byte) { ifc := bytesPool.Get() if ifc != nil { b = ifc.([]byte) } return } //  func putBytes(b []byte) { if cap(b) <= maxCap { b = b[:0] //  bytesPool.Put(b) } } 

覚えている


プールを使用する良い例は、 fmtパッケージです。 109から150行目まで。


Source: https://habr.com/ru/post/J277137/


All Articles