Haskell 実装が GC を使用する理由が知りたいです。
純粋な言語で GC が必要になるケースは考えられません。これは単にコピーを減らすための最適化なのでしょうか、それとも実際に必要なのでしょうか?
GC が存在しない場合、リークが発生するサンプル コードを探しています。
ベストアンサー1
すでに指摘されているように、Haskellでは自動、動的メモリ管理: 手動のメモリ管理は安全ではないため、自動メモリ管理が必要です。一部のプログラムでは、オブジェクトの有効期間を実行時にのみ決定できるため、動的メモリ管理が必要です。
たとえば、次のプログラムを考えてみます。
main = loop (Just [1..1000]) where
loop :: Maybe [Int] -> IO ()
loop obj = do
print obj
resp <- getLine
if resp == "clear"
then loop Nothing
else loop obj
[1..1000]
このプログラムでは、ユーザーが「クリア」と入力するまでリストはメモリに保持される必要があるため、このしなければならない動的に決定する必要があるため、動的メモリ管理が必要になります。
したがって、この意味では、自動化された動的メモリ割り当てが必要であり、実際には次のことを意味します。はいガベージ コレクションは最も高性能な自動動的メモリ マネージャーであるため、Haskell にはガベージ コレクターが必要です。
しかし...
ガベージコレクタは必要ですが、コンパイラがガベージコレクションよりも安価なメモリ管理スキームを使用できる特別なケースを見つけようとします。たとえば、
f :: Integer -> Integer
f x = let x2 = x*x in x2*x2
x2
が返ったときに が安全に解放できることをコンパイラが検出することを期待するかもしれないf
(ガベージコレクタが を解放するのを待つのではなくx2
)。本質的には、コンパイラに次のことを求めている。脱出分析ガベージコレクションされたヒープへの割り当てを変換するスタック上の割り当て可能な限り。
これはそれほど無理な要求ではない。jhc haskell コンパイラGHCはそうではないが、そうする。サイモン・マーロウ言うGHC の世代別ガベージコレクターにより、エスケープ分析はほとんど不要になります。
JHCは実際には、地域推定。 考慮する
f :: Integer -> (Integer, Integer)
f x = let x2 = x * x in (x2, x2+1)
g :: Integer -> Integer
g x = case f x of (y, z) -> y + z
この場合、単純なエスケープ解析では、x2
は から脱出するf
(タプルで返されるため) ため、ガベージ コレクションされたヒープに割り当てる必要があると結論付けられます。一方、領域推論では、 が返されるときに が割り当て解除できるx2
ことを検出できます。ここでの考え方は、は の領域ではなく の領域に割り当てる必要があるということです。x2
g
x2
g
f
Haskellを超えて
領域推論は上で述べたように特定のケースでは有用であるが、遅延評価と効果的に両立させることは難しいように思われる(エドワード・クメットのそしてサイモン・ペイトン・ジョーンズコメント)。例えば、
f :: Integer -> Integer
f n = product [1..n]
リスト[1..n]
をスタック上に割り当てて、f
戻った後に解放したいという誘惑に駆られるかもしれませんが、これは悲惨な結果をもたらします。O f
(1) メモリ (ガベージ コレクション下) の使用から O(n) メモリの使用に変更されるからです。
1990年代から2000年代初頭にかけて、地域推定に関する広範な研究が行われた。厳しい関数型言語ML。Mads Tofte、Lars Birkedal、Martin Elsman、Niels Hallenbergは、非常に読みやすい回顧地域推定に関する研究の多くを、MLKit コンパイラ彼らは、純粋にリージョンベースのメモリ管理 (つまり、ガベージ コレクタなし) とハイブリッド リージョンベース/ガベージ コレクション メモリ管理を実験し、テスト プログラムが純粋にガベージ コレクション バージョンよりも「10 倍高速から 4 倍低速」で実行されたと報告しました。