GNU Emacs 32.0.50 (9008bf64c3) のマニュアルを PLaMo 翻訳で機械翻訳したものです。誤訳を含むことがあります。原文: https://www.gnu.org/software/emacs/manual/html_node/elisp/Garbage-Collection.html ・ emacs-doc.conao3.com

E.2 ガベージコレクション

プログラムがリストを作成したり、ユーザーが(ライブラリのロードなどで)新しい関数を定義したりすると、そのデータは通常のストレージに配置されます。通常ストレージの空き容量が少なくなると、Emacsはオペレーティングシステムにメモリの追加割り当てを要求します。シンボル、コンスセル、スモールベクトル、マーカーなどの異なる種類のLispオブジェクトは、メモリ上の個別のブロックに分離して配置されます。(かなりサイズの大きいラージベクトル、長い文字列、バッファ、およびその他の特定の編集用オブジェクトは、オブジェクトごとに個別のブロックに割り当てられます。短い文字列は8kバイトのブロックに、スモールベクトルは4kバイトのブロックにパックされます)。

基本ベクトル以外にも、マーカーやオーバーレイ、バッファといった多くのオブジェクトが、あたかもベクトルであるかのように管理されています。対応するCのデータ構造にはunion vectorlike_headerフィールドが含まれており、そのsizeメンバにはenum pvec_typeで列挙されるサブタイプが格納されているほか、この構造体にLisp_Objectフィールドがいくつ含まれているか、および残りのデータのサイズに関する情報も保持されています。この情報はオブジェクトのメモリフットプリントを算出するために必要であり、ベクトル割り当てコードがベクトルブロックを反復処理する際に利用されます。

あるメモリ領域をしばらく使用した後、(例えば)バッファを解放したり、オブジェクトへの最後のポインタを削除したりして、その領域を開放することはよくあります。Emacsには、こうした放棄されたメモリ領域を回収するためのガベージコレクタが用意されています。ガベージコレクタは本質的に、Lispプログラムから依然としてアクセス可能なすべてのLispオブジェクトを特定し、マークすることで動作します。まず、すべてのシンボルとその値および関連する関数定義、ならびに現在スタック上にあるあらゆるデータがアクセス可能であると想定します。他のアクセス可能なオブジェクトを介して間接的に到達できるオブジェクトも同様にアクセス可能とみなされますが、この計算は「保守的」に行われるため、アクセス可能なオブジェクト数をわずかに過大評価することがあります。

マーキングが完了すると、まだマークされていないすべてのオブジェクトはガベージとなる。Lispプログラムやユーザーがどのような操作を行ったとしても、それらに到達する手段はもはや存在しないため、参照することは不可能である。誰もそれらを惜しむことはないため、その領域は再利用しても差し支えない。ガベージコレクタの第2段階(スイープ)では、それらの再利用が行われる。(ただし、マーキングは「保守的」に行われたため、1回のスイープですべての未使用オブジェクトが確実にガベージコレクションされるとは限らない。)

スイープフェーズでは、未使用の cons セルを将来の割り当て用にフリーリストに格納します。シンボルやマーカーについても同様です。アクセス可能な文字列をコンパクト化して占有する 8k ブロック数を減らし、その後、その他の 8k ブロックを解放します。ベクトルブロック内の到達不能なベクトルは統合され、可能な限り大きなフリー領域が作成されます。フリー領域が完全な 4k ブロックにまたがる場合、そのブロックは解放されます。そうでない場合、フリー領域はフリーリスト配列に記録され、各エントリは同一サイズの領域のフリーリストに対応します。大きなベクトルやバッファ、その他の大きなオブジェクトは、個別に割り当ておよび解放されます。

Common Lispに関する注記: 他のLispとは異なり、GNU Emacs Lispはフリーリストが空になってもガベージコレクタを呼び出しません。代わりに、単にオペレーティングシステムにさらなるメモリの割り当てを要求し、gc-cons-thresholdバイトが消費されるまで処理を継続します。

つまり、Lispプログラムの特定の箇所の直前に明示的にガベージコレクタを呼び出すことで、その箇所では2回目のガベージコレクションを強制するほどのメモリを消費しない限り、ガベージコレクタが実行されないようにすることができます。

関数: garbage-collect-heapsize

この関数は、現在のメモリ使用量に関する情報を返します。戻り値は使用領域量に関する情報を含むリストであり、各エントリは ‘(name size used)’ または ‘(name size used free)’ という形式になっています。エントリにおいて、name はそのエントリが表すオブジェクトの種類を示す記号、size は各オブジェクトが使用するバイト数、used はヒープ内で生存していることが判明したオブジェクトの数、そしてオプションの free は、生存はしていないが将来の割り当てのために Emacs が保持しているオブジェクトの数です。したがって、全体的な結果は以下の通りとなります:

((conses cons-size used-conses free-conses)
 (symbols symbol-size used-symbols free-symbols)
 (strings string-size used-strings free-strings)
 (string-bytes byte-size used-bytes)
 (vectors vector-size used-vectors)
 (vector-slots slot-size used-slots free-slots)
 (floats float-size used-floats free-floats)
 (intervals interval-size used-intervals free-intervals)
 (buffers buffer-size used-buffers)

以下に例を示します。

(garbage-collect)
      ⇒ ((conses 16 49126 8058) (symbols 48 14607 0)
                 (strings 32 2942 2607)
                 (string-bytes 1 78607) (vectors 16 7247)
                 (vector-slots 8 341609 29474) (floats 8 71 102)
                 (intervals 56 27 26) (buffers 944 8)
                 (heap 1024 11715 2678))

以下に、各要素について解説した表を示します。なお、最後の heap エントリは任意であり、基盤となる malloc 実装が mallinfo 関数を提供している場合にのみ存在します。

cons-size

consセルの内部サイズ、すなわち sizeof (struct Lisp_Cons)。

used-conses

使用中のコンソールセル数。

free-conses

オペレーティングシステムから領域を確保しているが、現在は使用されていないコンソールセルの数。

symbol-size

記号の内部サイズ、すなわち sizeof (struct Lisp_Symbol)。

used-symbols

使用されている記号の数。

free-symbols

オペレーティングシステムから領域を確保しているが、現在は使用されていないシンボルの数。

string-size

文字列ヘッダーの内部サイズ、すなわち sizeof (struct Lisp_String)。

used-strings

使用されている文字列ヘッダーの数。

free-strings

オペレーティングシステムから領域を確保しているが、現在は使用されていない文字列ヘッダーの数。

byte-size

これは便宜上用いられるもので、sizeof (char)と等価です。

used-bytes

すべての文字列データの合計サイズ(バイト単位)。

vector-size

ヘッダーを含む長さ1のベクトルのバイト数。

used-vectors

ベクトルブロックから割り当てられたベクトルヘッダの数。

slot-size

ベクトルスロットの内部サイズ。常に sizeof (Lisp_Object) となる。

used-slots

使用されているすべてのベクトルにおけるスロット数。プラットフォームによっては、スロット数にベクトルヘッダーのオーバーヘッドの一部または全部が含まれる場合があります。

free-slots

すべてのベクトルブロックにおける空きスロット数。

float-size

floatオブジェクトの内部サイズ、すなわち sizeof (struct Lisp_Float)。(プラットフォーム固有の float や double と混同しないようにしてください。)

used-floats

使用中のフロート数。

free-floats

オペレーティングシステムから領域を確保しているが、現在は使用されていないフロートの数。

interval-size

区間オブジェクトの内部サイズ、すなわち sizeof (struct interval)。

used-intervals

使用中のインターバル数。

free-intervals

オペレーティングシステムから領域を確保しているが、現在は使用されていないインターバルの数。

buffer-size

バッファの内部サイズ、すなわち sizeof (struct buffer)。(buffer-size 関数が返す値と混同しないでください。)

used-buffers

使用中のバッファオブジェクト数。これには、ユーザーからは見えない破棄済みのバッファ、すなわち all_buffers リスト内のすべてのバッファが含まれます。

unit-size

ヒープ領域の測定単位。常に1024バイトとなる。

total-size

ヒープの総サイズ(unit-size単位)。

free-size

現在使用されていないヒープ領域(unit-size単位)。

コマンド: garbage-collect

このコマンドはガベージコレクションを実行し、使用領域量に関する情報を返します。(前回のガベージコレクション以降、gc-cons-thresholdバイトを超えるLispデータを使用した場合、自動的にガベージコレクションが行われることもあります。)

garbage-collect は、garbage-collect-heapsize の場合と同様に、上記のリストを返します。

ユーザーオプション: garbage-collection-messages

この変数が nil 以外の場合、Emacs はガベージコレクションの開始時と終了時にメッセージを表示します。デフォルト値は nil です。

変数: post-gc-hook

これはガベージコレクションの終了時に実行される通常のフックです。フックの処理が実行されている間はガベージコレクションが抑制されるため、記述の際はご注意ください。

ユーザーオプション: gc-cons-threshold

この変数の値は、次のガベージコレクションをトリガーするために、1回のガベージコレクション後にLispオブジェクト用に割り当てる必要があるストレージのバイト数です。garbage-collect が返す結果を使用して、特定のオブジェクト型のサイズに関する情報を取得できます。なお、バッファの内容に割り当てられた領域はカウントされません。

初期の閾値は GC_DEFAULT_THRESHOLD であり、alloc.c で定義されています。これは word_size 単位で定義されているため、デフォルトの 32 ビット構成では 400,000、64 ビット構成および --with-wide-int オプションで設定された 32 ビットビルドでは 800,000 となります。より大きな値を指定すると、ガベージコレクションの頻度が低下します。これにより、ガベージコレクションに費やされる時間が短縮されます(そのため、ガベージコレクションの間隔が長くなり、Lisp プログラムの動作が高速化します)が、総メモリ使用量は増加します。大量の Lisp データを生成するプログラムを実行する場合、特に動作の高速化が必要な場合は、この設定を検討するとよいでしょう。ただし、長期間にわたって閾値を上げ続けることは推奨せず、プログラムが妥当な時間内に動作するために必要な値より高く設定しないようご注意ください。必要以上の閾値を使用すると、システム全体のメモリプレッシャーが高まる可能性があり、また各ガベージコレクションサイクルに要する時間が大幅に増加するため、避けるべきです。

GC_DEFAULT_THRESHOLDの10分の1までのより小さい値を指定することで、コレクションの頻度を高めることができます。この最小値を下回る値は、次回のガベージコレクションまでのみ有効となり、その時点でgarbage-collectによって閾値が最小値に戻されます。

ユーザーオプション: gc-cons-percentage

この変数の値は、ガベージコレクションが発生するまでのコンシング量を、現在のヒープサイズに対する割合で指定します。この基準とgc-cons-thresholdは並列に適用され、両方の基準が満たされた場合にのみガベージコレクションが発生します。

ヒープサイズが大きくなるにつれて、ガベージコレクションに要する時間が長くなります。したがって、それに比例して実行頻度を下げることが望ましい場合があります。

初期のパーセント値は、対話型セッションおよびEmacsのダンプ時(Building Emacsを参照)では0.1、非対話型(いわゆる「バッチ」)セッションでは1.0となります。

gc-cons-thresholdと同様に、必要以上に拡大せず、また長期間にわたって拡大しないでください。

gc-cons-threshold および gc-cons-percentage によるガベージコレクタの制御は、あくまで近似的なものです。Emacs は定期的に閾値の超過を確認しますが、効率上の理由から、ヒープや gc-cons-threshold、gc-cons-percentage に変更が加わるたびに即座に確認を行うわけではありません。そのため、閾値を超えても直ちにガベージコレクションがトリガーされることはありません。また、閾値計算の効率化のため、Emacs はヒープサイズを近似的に算出しており、その際、ヒープ内で現在アクセス可能なオブジェクトが使用しているバイト数をカウントしています。

garbage-collect が返す値は、データ型別に分類した Lisp データが使用するメモリ量を示します。これに対し、関数 memory-limit は、Emacs が現在使用しているメモリの総量に関する情報を提供します。

関数: memory-limit

この関数は、Emacsが現在使用している仮想メモリの総バイト数(1024で除算した値)の推定値を返します。これを利用することで、自身の操作がメモリ使用量にどのような影響を与えているか、大まかに把握することができます。

変数: memory-full

この変数は、EmacsのLispオブジェクト用メモリが不足しかけている場合は t、そうでない場合は nil となります。

関数: memory-use-counts

この関数は、現在のEmacsセッションで作成されたオブジェクトの数をカウントする数値のリストを返します。これらのカウンタはそれぞれ、特定の種類のオブジェクトごとにインクリメントされます。詳細はドキュメント文字列を参照してください。

関数: memory-info

この関数は、システム全体のメモリ容量と、そのうち空き容量を返します。サポート対象外のシステムでは、値が nil となる場合があります。

default-directory がリモートホストを指している場合、そのホストのメモリ情報が返されます。

変数: gcs-done

この変数には、今回のEmacsセッションでこれまでに実行されたガベージコレクションの総数が格納されています。

変数: gc-elapsed

この変数には、今回のEmacsセッションにおけるこれまでのガベージコレクションの経過時間(秒単位の合計)が、浮動小数点数として格納されています。

関数: memory-report

Emacsがどこでメモリを使用しているか(各種変数、バッファ、キャッシュなど)を確認することが有用な場合があります。このコマンドを実行すると、「最大」のバッファや変数を一覧表示するほか、概要を示す新しいバッファ(‘"*Memory Report*"’)が開きます。

ここにあるデータはすべて概算です。変数のサイズを算出する一貫した方法が実際には存在しないためです。例えば、2つの変数がデータ構造の一部を共有している場合、それは2回カウントされますが、それでもこのコマンドを使えば、Emacsのどの部分がメモリを使用しているかという有用な概況を把握できます。