コンパイラーが割り当て解除を自動的に挿入しないのはなぜですか?


63

Cのような言語では、プログラマはfreeへの呼び出しを挿入することが期待されています。コンパイラがこれを自動的に行わないのはなぜですか?人間は(バグを無視して)妥当な時間内にそれを行うので、不可能ではありません。

EDIT:今後の参考のために、ここで興味深い例があり、別の議論です。


125
そして、それは、子供たち、計算可能性理論を教える理由です。;)
ラファエル

7
人間もすべての場合に決定することはできないため、これは計算可能性の問題ではありません。これは完全性の問題です。割り当て解除ステートメントには、削除された場合、Cソースコードに含まれていない展開環境と予想される操作に関する情報が含まれていない限り、分析によって完全に回復できない情報が含まれます。
ナット

41
いいえ、それは計算可能性の問題です。特定のメモリの割り当てを解除するかどうかは決定できません。固定プログラムの場合、ユーザー入力やその他の外部干渉はありません。
アンドレイバウアー

1
コメントは詳細なディスカッション用ではありません。この会話はチャットに移動さました。質問に具体的に対処していないすべてのコメントとその改善方法は、すぐに削除されます。
ラファエル

2
@BorisTreukhov、チャットルームに持って行ってください。いいえ、Andrejがエスケープ分析は「不可能」であると言っているとは思いません(この文脈でそれが何を意味するかを正確に判断することは私には少し不明瞭です)。完全に正確なエスケープ分析決定不能です。すべて:チャットルームに持って行ってください。質問の改善を目的とするコメントのみをここに投稿してください。他のディスカッションやコメントはチャットルームに投稿する必要があります。
DW

回答:


80

プログラムがメモリを再び使用するかどうかは決定できないためです。これはfree()、すべての場合にいつ呼び出すべきかをアルゴリズムが正しく判断できないことを意味します。つまり、これを試みたコンパイラーは、メモリリークのあるプログラムや解放されたメモリを引き続き使用するプログラムを生成することを意味します。コンパイラーが2番目のコンパイラーを実行しないことを保証し、プログラマーがfree()これらのバグを修正するために呼び出しを挿入free()できるようにしたとしても、そのコンパイラーをいつ呼び出すかを知るfree()ことは、試みなかったコンパイラーを使用するときにいつ呼び出すかを知ることよりも困難です助けるために。


12
決定できない問題を解決する人間の能力をカバーする質問があります。コンパイラが使用するアルゴリズムに依存するため、誤ってコンパイルされるプログラムの例を挙げることはできません。しかし、アルゴリズムは無限に多くの異なるプログラムに対して誤った出力を生成します。
デビッドリチャービー

1
コメントは詳細なディスカッション用ではありません。この会話はチャットに移動さました
ジル「SO-悪であるのをやめる」

2
みんな、チャットに持って行ってください。回答自体に直接関係のないものや、改善方法はすべて削除されます。
ラファエル

2
コンパイラが喜んで行う多くのことは、一般的に決定できません。常にライスの定理に注意を払うなら、コンパイラの世界のどこにも行きません。
ティコンジェルビス

3
これは無関係です。すべてのコンパイラーにとって決定不能な場合、すべての人間にとっても決定不能です。それでも、人間がfree()正しく挿入することを期待しています。
ポールドレイパー

53

デイビッド・リチャービーが正しく指摘したように、この問題は一般的に決定不能です。オブジェクトの活性はプログラムのグローバルプロパティであり、一般にプログラムへの入力に依存します。

正確な動的 ガベージコレクションでさえ、決定できない問題です!すべての実際のガベージコレクターは、割り当てられたオブジェクトが将来必要になるかどうかの控えめな近似として到達可能性を使用します。これは良い近似ですが、それでも近似です。

しかし、それは一般的に真実です。コンピューターサイエンスビジネスで最も悪名高いコップアウトの1つは、「一般に不可能であるため、何もできない」ことです。それどころか、前進することが可能な多くの場合があります。

参照カウントに基づく実装は、「コンパイラが割り当て解除を挿入する」に非常に近いため、違いを見分けることは困難です。LLVM自動参照カウントObjective-CおよびSwiftで使用)は有名な例です。

領域の推論コンパイル時のガベージコレクションは、現在活発な研究分野です。MLMercuryなどの宣言型言語では、オブジェクトの作成後にオブジェクトを変更することはできません。

現在、人間のトピックに関して、人間が割り当ての有効期間を手動で管理する主な方法は3つあります。

  1. プログラムと問題を理解することにより。たとえば、人間は、同じ割り当てオブジェクトに同じ寿命のオブジェクトを置くことができます。コンパイラとガベージコレクタはこれを推測する必要がありますが、人間はより正確な情報を持っています。
  2. 必要な場合にのみ、非ローカルの簿記(参照カウントなど)または他の特別な割り当て手法(ゾーンなど)を選択的に使用する。繰り返しますが、コンパイラーが推測しなければならない場合、人間はこれを知ることができます。
  3. ひどく。誰もが結局、リークが遅い現実世界に展開されたプログラムを知っています。または、そうでない場合は、メモリの有効期間中にプログラムと内部APIを再構築する必要があり、再利用性とモジュール性が低下します。

コメントは詳細なディスカッション用ではありません。宣言的対機能的について議論したい場合は、チャットで行ってください。
ジル 'SO-悪であるのをやめる'

2
これは、質問に対する圧倒的な最良の回答です(あまりにも多くの回答では対処しません)。conserativeative GCに関するHans Boehmの先駆的な仕事への参照を追加できたかもしれません:en.wikipedia.org/wiki/Boehm_garbage_collector。もう1つの興味深い点は、抽象セマンティクスまたは実行モデルに関して、データの活性(または拡張された意味での有用性)を定義できることです。しかし、トピックは本当に広いです。
babou

29

これは不完全性の問題であり、決定不能の問題ではありません

割り当て解除ステートメントの最適な配置が決定できないことは事実ですが、それは単にここでの問題ではありません。人間にとってもコンパイラにとっても決定的ではないため、それが手動プロセスであるか自動プロセスであるかにかかわらず、最適な割り当て解除配置を常に意識的に選択することは不可能です。そして、完璧な人はいないので、十分に高度なコンパイラーは、ほぼ最適な配置を推測する際に人間よりも優れているはずです。そのため、明示的な割り当て解除ステートメントが必要な理由は決定不能です

外部の知識が割り当て解除ステートメントの配置を通知する場合があります。これらのステートメントを削除することは、操作ロジックの一部を削除することと同等であり、コンパイラーにそのロジックを自動生成するように依頼することは、考えていることを推測することと同等です。

たとえば、Read-Evaluate-Print-Loop(REPL)を書いているとします。ユーザーがコマンドを入力すると、プログラムがそれを実行します。ユーザーは、REPLにコマンドを入力することにより、メモリを割り当て/割り当て解除できます。ソースコードは、ユーザーがコマンドを入力したときの割り当て解除を含め、可能な各ユーザーコマンドに対してREPLが行うべきことを指定します。

しかし、Cソースコードが割り当て解除のための明示的なコマンドを提供しない場合、コンパイラは、ユーザーがREPLに適切なコマンドを入力したときに割り当て解除を実行する必要があると推測する必要があります。そのコマンドは「割り当て解除」、「無料」、または他の何かですか?コンパイラーには、コマンドの内容を知る方法がありません。そのコマンドワードを探すためにロジックでプログラムし、REPLがそれを見つけたとしても、ソースコードで明示的に指示しない限り、コンパイラは割り当て解除で応答する必要があることを知る方法がありません。

tl; dr 問題は、Cソースコードがコンパイラに外部知識を提供しないことです。プロセスが手動であるか自動化されているかにかかわらず、決定不能は問題ではありません。


3
コメントは詳細なディスカッション用ではありません。この会話はチャットに移動さました。この回答の欠点に具体的に対処していないコメントや修正方法については、すぐに削除されます。
ラファエル

23

現在、投稿された回答はどれも完全に正しいものではありません。

コンパイラーが割り当て解除を自動的に挿入しないのはなぜですか?

  1. ある人はそうします。(後で説明します。)

  2. 当然、free()プログラムが終了する直前に呼び出すことができます。しかし、あなたの質問にはfree()、できるだけ早く電話をかける必要があることが暗示されています。

  3. free()メモリに到達できなくなったらすぐにCプログラムを呼び出すという問題は決定できません。つまり、有限時間内に答えを提供するアルゴリズムでは、カバーされない場合があります。これ-および任意プログラムの他の多くの決定不能性-は停止問題から証明できます。

  4. 決定できない問題は、コンパイラであれ人間であれ、アルゴリズムによって常に有限時間で解決できるとは限りません。

  5. 人間は(自分自身で)アルゴリズムによってメモリの正確性を検証できる Cプログラムのサブセットを書き込もうとします。

  6. 一部の言語では、コンパイラに#5を組み込むことで#1を実現しています。メモリ割り当てを任意に使用するプログラムではなく、それらの決定可能なサブセットを許可します。FothRustは、Cよりもメモリの割り当てが制限されている言語の2つの例でありmalloc()、(1)決定可能セットの外側にプログラムが記述されているかどうかを検出できます(2)割り当て解除を自動的に挿入します。


1
私はRustがそれをどのように行うかを理解しています。しかし、私はこれをしたフォースのことを聞いたことがありません。詳しく説明してもらえますか?
ミルトンシルバ

2
@ MiltonSilva、Forth-少なくともその最も基本的な、元の実装-は、スタックではなく、ヒープを持っています。これは、コンパイラーが簡単に実行できるタスクである呼び出しスタックポインターを移動することにより、割り当て/割り当て解除を行います。Forthは非常に単純なハードウェアをターゲットにするように作られており、動的でないメモリで十分な場合もあります。それは明らかに、重要なプログラムのための実行可能な解決策ではありません。
ポールドレイパー

10

「人間がやるので、不可能ではない」というのはよく知られている誤りです。私たちが作成するものを必ずしも理解しているわけではありません(制御は言うまでもありません)。お金は一般的な例です。特に人的要因が存在しないと思われる場合、技術的な問題で成功する可能性を過大評価する(時には劇的に)傾向があります。

コンピュータープログラミングでの人間のパフォーマンスは非常に低く、コンピューターサイエンスの研究(多くの専門教育プログラムを欠いている)は、この問題に単純な修正がない理由を理解するのに役立ちます。いつか、おそらくそれほど遠くないところで、仕事の人工知能に取って代わられるかもしれません。それでも、常に自動的に割り当て解除を行う一般的なアルゴリズムはありません。


1
人間の誤りやすさの前提を受け入れ、それでも人間が作成した思考機械が依然として絶対的である(つまり人間よりも優れている)と仮定することの誤acy はあまり知られていないが、より興味深い。行動を進めることができる唯一の前提は、人間の心が完全に計算できる可能性があるということです。
ワイルドカード

1.思考マシンが絶対的だとは言わなかった。多くの場合、人間よりも優れています。2.行動の前提条件としての完全性(潜在性さえ)への期待は不条理です。
アンドレスーザレモス

「私たちはいつか、おそらくそれほど遠くないところに、仕事上の人工知能に取って代わられるかもしれません。」 これは、特に、ナンセンスです。人間はシステムの意図の源で​​す。人間がいなければ、このシステムには何の目的もありません。「人工知能」とは、過去のプログラマーやシステム設計者のインテリジェントな決定によって実際にもたらされた、マシンによるインテリジェントな現在の決定の外観として定義できます。メンテナンスがない場合(人が行う必要あります)、AI(または検査されずに完全に自動化されたままになっているシステム)は失敗します。
ワイルドカード

機械のように人間の意図は、常に外部から来ます。
アンドレスーザ

完全に真実ではありません。(また、「外部」はソースを定義しません)そのような意図は実際には存在しないと述べているか、意図は存在するがどこからでもないことを述べています。目的とは無関係に意図が存在する可能性があるとお考えですか?その場合、「意図」という言葉を誤解します。いずれにせよ、対面でのデモンストレーションは、この主題に関するあなたの心をすぐに変えるでしょう。言葉だけでは「意図」を理解することはできないため、このコメントの後に辞任します。したがって、ここでのさらなる議論は無意味です。
ワイルドカード

9

自動メモリ管理の欠如は、言語の機能です。

Cは、ソフトウェアを簡単に作成するためのツールではありません。これは、コンピューターに指示どおりに実行させるためのツールです。これには、選択した時点でのメモリの割り当てと割り当て解除が含まれます。Cは、コンピューターを正確に制御したい場合、または言語/標準ライブラリの設計者が期待したものとは異なる方法で物事を行いたい場合に使用する低レベル言語です。


コメントは詳細なディスカッション用ではありません。この会話はチャットに移動さました
DW

2
これは(CSの一部の)質問に対する回答ですか?
ラファエル

6
@Raphaelコンピューターサイエンスは、あいまいな技術的回答を探す必要があるという意味ではありません。コンパイラは、一般的なケースでは不可能な多くのことを行います。自動メモリ管理が必要な場合は、さまざまな方法で実装できます。Cはそうすべきではないため、そうしません。
ジョウニシレン

9

問題は主に歴史的な人工物であり、実装不可能ではありません。

ほとんどのCコンパイラがコードをビルドする方法は、コンパイラが一度に各ソースファイルのみを見るようにすることです。プログラム全体を一度に見ることはありません。あるソースファイルが別のソースファイルまたはライブラリから関数を呼び出すと、コンパイラは、関数の実際のコードではなく、関数の戻り値の型を持つヘッダーファイルのみを認識します。これは、ポインターを返す関数がある場合、ポインターが指しているメモリーを解放する必要があるかどうかをコンパイラーが判断する方法がないことを意味します。それを決定するための情報は、その時点ではコンパイラーに表示されません。一方、人間のプログラマは、関数のソースコードやドキュメントを自由に検索して、ポインタで何をする必要があるかを知ることができます。

C ++ 11やRustのような最新の低レベル言語を調べると、ポインターのタイプでメモリの所有権を明示的にすることで、ほとんどの問題が解決されていることがわかります。C ++ではunique_ptr<T>、プレーンの代わりにを使用T*してメモリを保持しunique_ptr<T>、プレーンとは異なり、オブジェクトがスコープの最後に到達するとメモリが解放されるようにしますT*。プログラマはメモリunique_ptr<T>を別のメモリに渡すことができますが、メモリをunique_ptr<T>指すのは1人だけです。そのため、誰がメモリを所有し、いつ解放する必要があるかは常に明確です。

C ++は、後方互換性の理由から、古いスタイルの手動メモリ管理を許可しているため、バグの作成やの保護を回避する方法が可能unique_ptr<T>です。Rustは、コンパイラエラーを介してメモリ所有権ルールを適用するという点で、さらに厳密です。

決定不能性、停止問題などについては、はい、Cのセマンティクスに固執すると、すべてのプログラムでメモリを解放する必要があるかどうかを判断することはできません。ただし、アカデミックな演習やバグのあるソフトウェアではなく、ほとんどの実際のプログラムでは、いつ解放するか、いつ解放しないかを決定することは絶対に可能です。そもそも、人間がいつ解放すべきかを判断できる唯一の理由です。


コメントは詳細なディスカッション用ではありません。この会話はチャットに移動さました
ラファエル

6

他の答えは、ガベージコレクションを実行できるかどうか、実行方法の詳細、および問題のいくつかに焦点を当てています。

まだカバーされていない問題の1つは、ガベージコレクションの避けられない遅延です。Cでは、プログラマがfree()を呼び出すと、そのメモリはすぐに再利用できます。(少なくとも理論的には!)プログラマーは100MB構造を解放し、ミリ秒後に別の100MB構造を割り当て、全体のメモリ使用量が同じままになることを期待できます。

これは、ガベージコレクションには当てはまりません。ガベージコレクションされたシステムでは、未使用のメモリをヒープに戻すのに多少の遅延があります。100MB構造が範囲外になり、1ミリ秒後にプログラムが別の100MB構造をセットアップした場合、システムが短時間200MBを使用していると合理的に予想できます。その「短い期間」は、システムに応じてミリ秒または秒になる場合がありますが、まだ遅延があります。

RAMと仮想メモリのギグを備えたPCで実行している場合、もちろんこれに気付かないでしょう。ただし、リソースが限られているシステム(組み込みシステムや電話など)で実行している場合は、これを真剣に検討する必要があります。これは単なる理論的なものではありません。WinCEシステムで.NET Compact Frameworkを使用してC#で開発しているときに、これが(デバイスの種類の問題をクラッシュさせるなどの)問題を引き起こすことを個人的に見てきました。


理論的には、すべての割り当ての前にGCを実行できます。
adrianN

4
@adrianNしかし、実際にはこれはメンタルになるため、実行されません。Grahamのポイントは今でも有効です。GCには、ランタイムの観点から、または必要な余剰メモリの観点から、常にかなりのオーバーヘッドが発生します。このバランスを極端に調整することはできますが、基本的にオーバーヘッドを取り除くことはできません。
コンラッドルドルフ

メモリが解放されるときの「遅延」は、リソースが限られているシステムよりも仮想メモリシステムの方が問題です。前者の場合、システムに200MBの空きがある場合でも、プログラムが200MBよりも100MBを使用する方がよい場合がありますが、後者の場合、一部の期間中に遅延が許容されない限り、必要以上に早くGCを実行するメリットはありません他の部分よりもコードの部分。
-supercat

これが(CSの一部の)質問にどのように答えようとするかわかりません。
ラファエル

1
@Raphaelガベージコレクションの原理に関するよく知られた問題を説明しました。これは、CSの基本的な欠点の1つとして教えられています(または教えられるべきです)。これが実際に理論的な問題ではないことを示すために、実際にこれを見たという私の個人的な経験さえ与えました。あなたがこれについて何かを理解できなかった場合、私はあなたに話をして問題の知識を向上させてうれしいです。
グラハム

4

この問題は、割り当て解除がプログラマがソースコードの他の部分から推測するものであると仮定しています。そうではありません。「プログラムのこの時点で、メモリー参照FOOはもう役に立たない」とは、割り当て解除ステートメントに(手続き言語で)エンコードされるまでプログラマーの心にしかわからない情報です。

理論的には他のコード行と違いはありません。コンパイラが「プログラムのこの時点で、レジスタBARの入力を確認する」または「関数呼び出しがゼロ以外を返した場合、現在のサブルーチンを終了する」を自動的に挿入しないのはなぜですか?コンパイラの観点から、この答えに示されているように、理由は「不完全」です。しかし、プログラマーが自分の知っていることをすべて伝えなかった場合、どのプログラムも不完全性に悩まされます。

実際には、割り当て解除は単調な作業または定型的なものです。私たちの脳は自動的にそれらを埋め、それについて不平を言い、「コンパイラーはそれと同じかそれ以上にできる」という感情は真実です。しかし、理論的にはそうではありませんが、幸いなことに他の言語はより多くの理論の選択肢を与えてくれます。


4
「「プログラムのこの時点で、メモリ参照FOOはもう役に立たない」とは、プログラマーの心にしか知られていない情報です」-それは明らかに間違っています。a)多くのFOOにとって、これを理解することは簡単です。たとえば、値のセマンティクスを持つローカル変数です。b)プログラマは常にこれを知っていることをお勧めします。これは明らかに過度に楽観的な仮定です。それが本当なら、メモリの取り扱いがセキュリティ上重要なため、重大なバグはありません。悲しいかな、私たちはそうします。
ラファエル

私は、プログラマー FOOがもう役に立たないことを知っている場合のために言語が設計されたことを提案しています。明らかに、これは通常真実ではないことに同意します。そのため、静的分析やガベージコレクションが必要です。ほらね しかし、OPの問題は、これらのこといつハンドコーディングされたdeallocsほど価値がないのかということです。
トラビスウィルソン

4

何をされ、ガベージコレクションがあり、かつ参照カウント(のObjective-C、スウィフト)を使用して、コンパイラがあります行って。参照カウントを行うものは、強力な参照サイクルを回避することにより、プログラマーの助けが必要です。

本当の答えそのコンパイラの作家は十分に良いと、コンパイラでは、それを使用可能にするのに十分な速さで道を考え出したていないです「なぜ」。コンパイラライターは通常非常に賢いので、十分に適切で高速な方法を見つけるのは非常に難しいと結論付けることができます。

それが非常に、非常に難しい理由の1つは、もちろんそれが決定できないことです。コンピュータサイエンスでは、「決定可能性」について話すとき、「正しい決定を下す」ことを意味します。もちろん、人間のプログラマーは、正しい決定に限定されないため、メモリーの割り振り解除の場所を簡単に決定できます。そして、彼らはしばしば間違った決定を下します。


私はここに貢献を見ません。
babou

3

Cのような言語では、プログラマはfreeへの呼び出しを挿入することが期待されています。コンパイラがこれを自動的に行わないのはなぜですか?

メモリブロックの寿命は、コンパイラではなくプログラマの決定であるためです。

それでおしまい。これはCの設計です。コンパイラは、メモリブロックを割り当てる意図が何であるかを知ることができません。人間はそれを行うことができます。なぜなら、彼らはすべてのメモリブロックの目的を知っているからです。それは、書かれているプログラムの設計の一部です。

Cは低レベル言語であるため、メモリのブロックを別のプロセスまたは別のプロセッサに渡すことは非常に頻繁です。極端な場合、プログラマは意図的にメモリのチャンクを割り当て、システムの他の部分にメモリのプレッシャーをかけるためだけに再び使用することはありません。コンパイラには、ブロックがまだ必要かどうかを知る方法がありません。


-1

Cのような言語では、プログラマはfreeへの呼び出しを挿入することが期待されています。コンパイラがこれを自動的に行わないのはなぜですか?

Cや他の多くの言語では、コンパイル時に実行すべきことが明らかな場合に、コンパイラにこれと同等の機能を実行させる機能があります。自動期間変数(通常のローカル変数)の使用。コンパイラーは、そのような変数に十分なスペースを配置し、それらの(明確に定義された)ライフタイムが終了したときにそのスペースを解放する責任があります。

C99以降、可変長配列はCの機能であるため、自動期間オブジェクトは、原則として、計算可能な期間の動的に割り当てられたオブジェクトが行うCのほぼすべての機能を提供します。もちろん、実際には、C 実装はVLAの使用に大幅な実用上の制限を課す可能性があります(つまり、スタックに割り当てられた結果としてサイズが制限される場合があります)が、これは言語設計の考慮事項ではなく、実装の考慮事項です。

意図された使用法で自動期間を与えることができないオブジェクトは、正確には、コンパイル時に寿命を決定できないオブジェクトです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.