プログラムの有効期間中にメモリを使用する必要がある場合、プログラムの終了直前にメモリを解放する必要は本当にありますか?


67

多くの本やチュートリアルで、メモリ管理の実践が強調されていることを聞き、使用後にメモリを解放しないと、不可解で恐ろしいことが起こると感じました。

私は他のシステムについて話すことはできません(私にとっては同様のプラクティスを採用していると仮定するのは合理的です)が、少なくともWindowsでは、カーネルは基本的にほとんどのリソース(奇数を除いて)をクリーンアップすることを保証されていますプログラム終了後のプログラム。これには、ヒープメモリなどが含まれます。

ユーザーが利用できるようにするためにファイルを使い終わった後にファイルを閉じたい理由や、帯域幅を節約するためにサーバーに接続されたソケットを切断したい理由を理解していますが、プログラムが使用するすべてのメモリをマイクロ管理する必要があります。

今、私はこの質問はあなたが必要とどのくらいのメモリに基づいており、あなたの記憶を処理する方法以来、幅広いであることに同意し、あなたがそれを必要とするとき、私はこれまで、この質問の範囲を狭めるます:私はの作品を使用する必要がある場合プログラムの存続期間中のメモリ、プログラム終了直前にメモリを解放する必要は本当にありますか?

編集:重複として示唆された質問は、オペレーティングシステムのUnixファミリーに固有のものでした。その一番の答えは、Linux固有のツール(Valgrindなど)でさえも特定しました。この質問は、ほとんどの「通常の」非組み込みオペレーティングシステムと、プログラムの寿命を通じて必要とされるメモリを解放するのが良い習慣であるか、またはそうでない理由を網羅することを意図しています。



19
この言語に依存しないタグを付けましたが、多くの言語(javaなど)では、メモリを手動で解放する必要はまったくありません。オブジェクトへの最後の参照が範囲外になった後、しばらくして自動的に発生します
リチャードティングル

3
そしてもちろん、1回も削除せずにC ++を書くことができ、メモリには問題ありません
日興

5
@RichardTingle CやC ++以外の言語は考えられませんが、言語に依存しないタグは、ガベージコレクションユーティリティが組み込まれていないすべての言語をカバーすることを目的としていました。それは最近では確かにまれですが。このようなシステムをC ++で実装できるようになったと主張することもできますが、最終的にはメモリの一部を削除しないという選択肢があります。
CaptainObvious

4
「カーネルは、プログラムの終了後にすべてのリソースをクリーンアップすることが基本的に保証されています」すべてがメモリ、ハンドル、またはカーネルオブジェクトではないため、これは一般に間違っています。しかし、あなたの質問をすぐに制限する記憶については真実です。
エリックタワーズ

回答:


108

プログラムの有効期間中にメモリを使用する必要がある場合、プログラムの終了直前にメモリを解放する必要は本当にありますか?

これは必須ではありませんが、利点(およびいくつかの欠点)があります。

プログラムが実行時にメモリを1回割り当て、プロセスが終了するまでメモリを解放しない場合は、メモリを手動で解放してOSに依存しないのが賢明なアプローチです。私が知っている現代のすべてのOSでは、これは安全です。プロセスの最後に、割り当てられたすべてのメモリが確実にシステムに返されます。
場合によっては、割り当てられたメモリを明示的にクリーンアップしない方が、クリーンアップを実行するよりも著しく高速になる場合があります。

ただし、実行の最後にすべてのメモリを明示的に解放することにより、

  • デバッグ/テスト中に、メモリリーク検出ツールが「誤検知」を表示しない
  • メモリを割り当てと割り当て解除とともに使用するコードを別のコンポーネントに移動し、後でメモリの使用時間をコンポーネントのユーザーが制御する必要がある別のコンテキストで使用する方がはるかに簡単かもしれません

プログラムの寿命は変わる可能性があります。たぶんあなたのプログラムは典型的な寿命が10分未満の小さなコマンドラインユーティリティであり、10秒ごとに数kbの部分でメモリを割り当てます-したがって、プログラムが終了する前に割り当てられたメモリを解放する必要はありません。後でプログラムが変更され、数週間の寿命を持つサーバープロセスの一部として拡張使用法が取得されます-そのため、間に未使用メモリを解放しないことはもはやオプションではありません。そうしないと、プログラムは利用可能なすべてのサーバーメモリを徐々に使い果たします。つまり、プログラム全体を確認し、その後で割り当て解除コードを追加する必要があります。運がよければ、これは簡単な作業です。そうでなければ、場所を逃す可能性が高いほど難しいかもしれません。そして、あなたがその状況にいるとき、あなたはあなたが「無料」を追加したことを望むでしょう

より一般的には、割り当ておよび関連する割り当て解除コードを書くことは、多くのプログラマーの間で常にペアワイズを「良い習慣」としてカウントします。これを常に行うことにより、メモリを解放する必要がある状況で割り当て解除コードを忘れる可能性を減らします。


12
優れたプログラミング習慣を開発することの良い点。
ローレンス

4
一般的に、私はこのアドバイスに強く同意します。記憶と良い習慣を保つことは非常に重要です。ただし、例外があると思います。たとえば、割り当てたものが、「適切に」移動して解放するのに数秒かかるクレイジーなグラフである場合、プログラムを終了してOSを無効にするだけで、ユーザーエクスペリエンスが向上します。
GrandOpener

1
それはすべての現代の「通常の」(メモリ保護が組み込まれていない)OSで安全で、そうでなければ深刻なバグになります。特権のないプロセスが、OSが回収しないメモリを永久に失う可能性がある場合、何度も実行すると、システムがメモリ不足になる可能性があります。OSの長期的な健全性は、非特権プログラムのバグの欠如に依存することはできません。(もちろん、これはUnix共有メモリセグメントのようなものを無視します。これはせいぜいスワップスペースによってのみサポートされます。)
Peter Cordes

7
@GrandOpenerこの場合、通常の方法で割り当てることができ、時が来たら領域全体を一度に割り当て解除できるように、このツリーに何らかの種類の領域ベースのアロケーターを使用することを好むかもしれません少しずつ。それはまだ「適切」です。
トーマス

3
さらに、メモリがプログラムの全ライフタイムにわたって本当に存在する必要がある場合、スタック上に構造体を作成するのが賢明な代替手段かもしれませんmain
カイルストランド

11

プログラム実行の終了時にメモリを解放することは、CPU時間の無駄です。それは、軌道からそれを消す前に家を片付けるようなものです。

ただし、実行時間の短いプログラムが、実行時間の長いプログラムの一部に変わる場合があります。その後、ものを解放することが必要になります。これが少なくともある程度考慮されていなかった場合、大幅な再作業が必要になる可能性があります。

これに対する賢明な解決策の1つは、「talloc」です。これにより、メモリを大量に割り当て、1回の呼び出しでそれらをすべて破棄できます。


1
OSに独自のリークがないことがわかっていると仮定します。
WGroleau

5
「CPU時間の浪費」とはいえ、非常に短い時間です。free通常は、よりもはるかに高速ですmalloc
ポールドレーパー

@PaulDraper Aye、すべてを元に戻す時間の無駄はずっと重要です。
デュプリケータ

5

ガベージコレクションで言語を使用できます(Scheme、Ocaml、Haskell、Common Lisp、さらにはJava、Scala、Clojureなど)。

(ほとんどのGC対応言語では明示的かつ手動でメモリ解放する方法はありません!場合によっては、GCとランタイムシステムがその値に到達できないときにファイルハンドル値を閉じるなど、一部の値がファイナライズされることがありますが、これはそうではありません確実で信頼性が低く、代わりに明示的にファイルハンドルを閉じる必要があります。ファイナライズは保証されないためです)

また、C(またはC ++でも)でコーディングされたプログラムでは、Boehmの保守的なガベージコレクターを使用できます。あなたは、すべてあなたの置き換えますmallocGC_malloc について気にしていないfree任意のポインタを-ing。もちろん、BoehmのGCを使用することの長所と短所を理解する必要があります。GCハンドブックもお読みください。

メモリ管理は、プログラムのグローバルプロパティです。何らかの方法で、それ(および特定のデータの活性)は、プログラム全体のプロパティであるため、非構成的で非モジュラーです。

最後に、他の人が指摘したように、freeヒープに割り当てられたCメモリゾーンを明示的に使用することをお勧めします。大量のメモリを割り当てないおもちゃのプログラムの場合は、freeメモリをまったく使用しないこともできます(プロセスが終了すると、仮想アドレススペースなどのリソースがオペレーティングシステムによって解放されるため)。

プログラムの有効期間中にメモリを使用する必要がある場合、プログラムの終了直前にメモリを解放する必要は本当にありますか?

いいえ、そうする必要はありません。そして、多くの現実世界のプログラムは、寿命全体に必要なメモリを解放しません(特にGCCコンパイラはメモリの一部を解放しません)。ただし、それを行う場合(たとえばfree、特定のCの動的に割り当てられたデータを気にしない場合)、その事実をコメントして、同じプロジェクトで将来のプログラマの作業容易にすることができます。解放されていないメモリの量は制限されたままにしておくことをお勧めしますが、通常は、使用済みヒープメモリの合計に対して比較的小さいwrtです。

free多くの場合、システムはメモリをOSに解放しません POSIXシステムでmunmap(2)を呼び出すなど)が、通常はfutureによってメモリゾーンを再利用可能としてマークしますmalloc。特に、仮想アドレス空間(たとえば、/proc/self/mapsLinuxで見られるように、proc(5)を参照)は縮小されない場合がfreeあります(したがって、プロセスで使用されるメモリ量と同じ量のユーティリティが報告される)。pstop


3
「場合によっては、一部の値が確定する場合があります。たとえば、GCとランタイムシステムは、その値に到達できない場合にファイルハンドル値を閉じます。」これまで任意のファイナライザはさらに終了時に、実行します。stackoverflow.com/questions/7880569/...
デュプリケータ

私は個人的にこの答えを好みます。GCの使用を推奨しているからです(つまり、より自動化されたアプローチ)。
Taのタンディン

3
@tathanhdinhより自動化されていますが、メモリ専用です。他のすべてのリソースについては、完全に手動です。GCは、メモリの便利な処理のために決定論とメモリを交換することによって機能します。いいえ、ファイナライザーはあまり役に立たず、独自の問題があります。
デデュプリケーター

3

あなたが失敗した場合、あなたはあなたのプログラムを適切に実行するのに失敗しないので、それは必要ではありません。ただし、機会があれば、選択できる理由があります。

私が(何度も)遭遇する最も強力なケースの1つは、誰かが実行可能ファイルで実行される小さなシミュレーションコードを書くことです。彼らは「このコードをシミュレーションに統合したい」と言っています。それから、モンテカルロの実行間でどのように再初期化するのかを彼らに尋ねると、彼らはぼんやりと私を見ます。「再初期化とはどういう意味ですか。プログラムを新しい設定で実行するだけですか?」

クリーンクリーンアップを行うと、ソフトウェアの使用がはるかに簡単になる場合があります。多くの例の場合、何かをクリーンアップする必要はないと想定し、データを処理する方法とそれらの推定に関するデータの寿命について仮定します。これらの推定が有効ではない新しい環境に移行すると、アルゴリズム全体が機能しなくなる可能性があります。

奇妙なことがどのように発生するかという例については、管理言語がプロセスの最後にファイナライズを処理する方法、またはC#がアプリケーションドメインのプログラムによる停止を処理する方法を見てください。これらの極端な場合には亀裂を通過する仮定があるため、それらは結び目で結ばれています。


1

とにかく手動でメモリを解放しない言語を無視する...

あなたが今「プログラム」と考えているものは、ある時点で、より大きなプログラムの一部である単なる関数またはメソッドになるかもしれません。そして、その関数は複数回呼び出されるかもしれません。そして、「手動で解放」すべきメモリはメモリリークになります。それはもちろん判断の呼び出しです。


2
これは、数時間前に投稿されたトップアンサーで指摘された(そしてよりよく説明された)単なる繰り返しのようです:メモリの使用時間をコンポーネントのユーザーが制御する必要がある異なるコンテキストで...」
gnat

1

いつかあなたのプログラムを変更したいと思う可能性が非常に高いので、おそらくそれを他の何かと統合し、いくつかのインスタンスを順番にまたは並行して実行するでしょう。その後、このメモリを手動で解放する必要がありますが、状況を思い出せなくなり、プログラムを再理解するのに多くの時間がかかります。

それらについてのあなたの理解がまだ新鮮な間に物事を行います。

将来的に大きなリターンをもたらす可能性のある小さな投資です。


2
これは、前の11の回答で作成および説明されたポイントに対して実質的なものを追加しないようです。具体的には、おそらく将来的にプログラムを変更することについてのポイントは、すでに3〜4回行われている
ブヨ

1
@gnat複雑で曖昧な方法で-はい。明確な声明で-いいえ。迅速さではなく、回答の質に焦点を当てましょう。
Agent_L

1
品質面では、トップの答えは、この点を説明するにははるかに良いように見える
ブヨ

1

いいえ、それは必要ではありませんが、それは良いアイデアです。

「使い終わった後、メモリを解放しなければ、神秘的で恐ろしいことが起こるだろう」とあなたは言う

技術的に言えば、これの唯一の結果は、ハード制限に達するまで(たとえば、仮想アドレス空間が使い果たされるまで)、プログラムがより多くのメモリを消費し続けるか、パフォーマンスが許容できなくなることです。プログラムが終了しようとしている場合、プロセスは事実上存在しなくなるため、これは問題になりません。「神秘的で恐ろしいもの」は、純粋に開発者の精神状態に関するものです。メモリリークの原因を見つけることは絶対的な悪夢(これは控えめな表現です)である可能性があり、リークのないコードを書くには多くのスキルと規律が必要です。このスキルと規律を開発するための推奨されるアプローチは、プログラムが終了しようとしている場合でも、不要になったらメモリを常に解放することです。

もちろん、これには、他の人が言ったように、コードをより簡単に再利用して適用できるという追加の利点があります。

ただし、プログラムの終了直前にメモリを解放しない方が良い場合が少なくとも1つあります。

何百万もの小さな割り当てを行い、ほとんどがディスクにスワップされた場合を考えてください。すべてを解放し始めると、メモリのほとんどがRAMにスワップバックされて、簿記情報にアクセスできるようになり、ただちにデータを破棄する必要があります。これにより、プログラムが終了するまでに数分かかることがあります!言うまでもなく、その間、ディスクと物理メモリに大きな負荷がかかります。物理メモリが不足している場合(別のプログラムが大量のメモリを消費しているためにプログラムが閉じられている可能性があります)、複数のオブジェクトを同じページですが、連続して解放されません。

代わりにプログラムが中止される場合、OSはディスクにスワップされたすべてのメモリを単に破棄します。これは、ディスクアクセスをまったく必要としないため、ほぼ瞬時に行われます。

オブジェクト指向言語では、オブジェクトのデストラクタを呼び出すと、メモリが強制的にスワップインされることに注意することが重要です。これを行う必要がある場合は、メモリを解放することもできます。


1
割り当てを解除するには、ページアウトされたメモリをページインする必要があるという考えをサポートするものを引用できますか?それは明らかに非効率的で、確かに最新のOSはそれよりも賢いのです!

1
@Jon of All Tradesでは、現代のOSはスマートですが、皮肉なことにほとんどの現代言語はそうではありません。あなたが(何か)を解放すると、コンパイラはスタックに「何か」を置き、free()を呼び出します。スタックに置くには、スワップアウトされた場合、RAMに戻す必要があります。
ジェフィーキンズ

@JonofAllTradesフリーリストはその効果を持ちますが、それはかなり時代遅れのmalloc実装です。しかし、OSはそのような実装の使用を妨げることはできません。

0

手動でクリーンアップする主な理由は、終了時にクリーンアップされるメモリブロックの本物のメモリリークを間違える可能性が低いことです。割り当てを解除すると、プログラムを終了する前に一部のオブジェクトの割り当てを解除する必要あるようにリファクタリングする場合に、頭痛がはるかに小さくなります。

手動でクリーンアップしない主な理由は次のとおりです。パフォーマンス、パフォーマンス、パフォーマンス、および不要なクリーンアップコードでの何らかのフリーツーまたはフリーアフターバグの可能性。クラッシュバグまたはセキュリティに変わる可能性があります。エクスプロイト。

パフォーマンスが気になる場合は、推測するのではなく、常にプロファイルを作成して、どこで無駄になっているのかを見つけたいと思うでしょう。妥協の可能性としては、オプションのクリーンアップコードを条件付きブロックにラップし、デバッグ時にそのままにして、コードの記述とデバッグの利点を得ることができます。そして、経験的にそれが多すぎると判断した場合にのみオーバーヘッド、最終的な実行可能ファイルでスキップするようコンパイラーに指示します。そして、プログラムを閉じることの遅れは、ほとんど定義上、クリティカルパスではほとんどありません。

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