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行目まで。







All Articles