開発をしていてあまりに気にしないが重要なアロケータや、ガベージコレクションについて解説をしていきます。
アロケータ
Goのアロケータはオブジェクトを操作するのに必要なメモリブロックを動的に割り当てられ、ロックや断片化を回避してOSヘの遅いシステムコールを軽減できる。
GoのアロケータはTCMallocを厳密にモデル化したアロケータである。
TCMalloc
TCMallocは、マルチスレッド時代に合わせて設計されているmalloc(動的メモリを確保する)実装で、メモリを複数のレベルに分割してロックの粒度を下げる。
メモリのロックはメモリがスワップエリアにページングされるのを防ぐ。ページングは予期しないプログラムの実行遅延をもたらす原因になる。
メモリ管理は以下の2つに分かれている
- スレッドメモリ
- ページヒープ
スレッドメモリ
各メモリーページは、複数の固定割り当て可能サイズのクラスの空きリストに分割され、断片化の削減に役立ちます。各スレッドにはロックのない小さなオブジェクトのキャッシュがあり、並列プログラムで小さなオブジェクト(32KB以下)を割りてるのが非常に効率的になる。
ページヒープ
GCMallocによって管理されるヒープはページコレクションで構成される。割り当てられたオブジェクトが32KBより大きい場合、割り当てにはページヒープが使用される。
小さなオブジェクトを割り当てるためのメモリが足りてない場合は、ページヒープにメモリを割り当てる。メモリが足りてな場合、ページヒープはOSにさらにメモリを要求する。
このように割り当てることで、ユーザー空間のメモリプール*1を維持するため、メモリの割り当てと解放の効率が良くなる。
*1:メモリプールは、コンピュータプログラムの実行時に素早く動的に必要なメモリ領域を確保するために、プログラムの起動時などにメインメモリの空き領域からある程度大きな領域を一括して確保したもの。
ガベージコレクション
Goのアロケータを使ってメモリブロックのメモリを効率的に確保できますが、メモリブロックを削除できなければ無制限にメモリを確保ができないです。そのためにガーベージコレクション(GC)というプログラムのヒープから未使用のオブジェクトを取り除く処理をするバックグランドルーチンがある。
GCの回収のタイミングが大事で、GCの実行頻度が低いとゴミ(未使用オブジェクト)がたまって、メモリがいっぱいになってしまう。逆に頻度が高いと、機能を実行する代わりにプログラム時間とCPUのほとんどをGCの実行に使ってしまう可能性がある。
GCの実行間隔は時間に基づくものではなく、GOGC、GOMEMLIMITの2つの変数によって決まる。
GOGCはデフォルト100で設定されていて、ヒープサイズが最後のGCサイクルの終了時に持っているサイズの100%に拡張されたときに、次のGCの回収が行われる。
GOMEMLIMITはGo1.19で導入され、デフォルトでは無効になっている。設定されたメモリ制限に近いまたは超えている場合、頻繁にGCを実行する。
理解度チェック
今回の内容がわかっているかの問題を作成しています。
問題をクリックすると、答えが表示されますので、今回の内容を理解できたかチェックをしてみてください。
まとめ
今回はメモリ管理に必要なアロケータとガベージコレクションについて解説をしてきました。
仮想記憶というメモリ管理の基本や、Goについての記事が他にもありますので、こちらも読んでもらえたらと思います。
【おすすめ記事のリンク】
コメント