メモリリークはどこまで行くことができますか?


118

メモリリークに何度も遭遇しました。通常、私はmalloc明日がないようなとき、またはFILE *汚れた洗濯物のようにぶら下がっているとき。私は通常、少なくともプログラムが終了したときにすべてのメモリがクリーンアップされると想定しています(読む:必死に願っています)。プログラムの終了時やクラッシュ時に、リークされたメモリが収集されない状況はありますか?

答えが言語によって大きく異なる場合は、C(++)に焦点を当てましょう。

「明日がないような」、「汚れた洗濯物のようにぶら下がっている」などの語句の双曲線の使用に注意してください。安全ではない* malloc*あなたが愛する人を傷つける可能性があります。また、汚れた洗濯にはご注意ください。


3
LinuxやWindowsのような「最新の」OSで実行している場合、プログラムが終了すると、OS自体が未解放のメモリを解決します。
Oliver Charlesworth 2013年

60
明日がないようにmalloc-ingする代わりに、明日があるふりをして、あなたの記憶を追跡してください!
William Pursell 2013年

8
@WilliamPursellああ、だからcalloc、明日はないようにすべきだと言っている。優れた。
DilithiumMatrix 2013年

8
「答えが言語によって大きく異なる場合は、c(++)に焦点を当てましょう。」cc ++は同じ言語ではありません!
Johnsyweb 2013年

11
@zhermes:CとC ++が異なる言語であることについてのコメントは、想像以上に隠されています... C ++では、自動ストレージ期間を持つオブジェクトを活用し、RAIIイディオムに従ってください...これらのオブジェクトにメモリを任せますあなたのための管理。
LihO 2013年

回答:


111

いいえ。オペレーティングシステムは、プロセスが終了すると、プロセスが保持していたすべてのリソースを解放します。

これは、オペレーティングシステムが維持するすべてのリソースに適用されます:メモリ、開いているファイル、ネットワーク接続、ウィンドウハンドル...

とはいえ、プログラムがオペレーティングシステムのない組み込みシステム、または非常に単純またはバグのあるオペレーティングシステムで実行されている場合、メモリは再起動するまで使用できなくなる可能性があります。しかし、もしあなたがそのような状況にあったなら、あなたはおそらくこの質問をすることはないでしょう。

オペレーティングシステムは、特定のリソースを解放するのに長い時間がかかることがあります。たとえば、ネットワークサーバーが接続を受け入れるために使用するTCPポートは、プログラムによって適切に閉じられていても、解放されるまでに数分かかる場合があります。ネットワーク化されたプログラムは、データベースオブジェクトなどのリモートリソースも保持できます。リモートシステムは、ネットワーク接続が失われたときにこれらのリソースを解放する必要がありますが、ローカルオペレーティングシステムよりもさらに時間がかかる場合があります。


5
RTOSの一般的なパラダイムは、シングルプロセス、マルチスレッドモデルであり、「タスク」間のメモリ保護はありません。通常は1つのヒープがあります。これは確かにVxWorksが以前は機能していた方法であり、おそらくまだ機能しています。
marko 2013年

29
オペレーティングシステムがすべてのリソースを解放できるわけではないことに注意してください。ネットワーク接続、データベーストランザクションなど、明示的に閉じない場合は、望ましくない結果が生じる可能性があります。ネットワーク接続を閉じない場合、サーバーは無期限にアクティブであるとサーバーに思わせる可能性があり、アクティブな接続の数を制限しているサーバーでは、誤ってサービス拒否を引き起こす可能性があります。データベーストランザクションを閉じないと、コミットされていないデータが失われる可能性があります。
リーライアン

1
@Marko:最近のバージョンのvxWorksは、メモリ保護をサポートするRTP(リアルタイムプロセス)をサポートするようになりました。
ザビエルT.

20
「オペレーティングシステムは、プロセスが終了すると、プロセスが保持していたすべてのリソースを解放します。」厳密には当てはまりません。たとえば、(少なくとも)Linuxでは、SysVセマフォとその他のIPCオブジェクトはプロセスの終了時にクリーンアップされません。これがipcrm、手動によるクリーンアップlinux.die.net/man/8/ipcrmがある理由です。
sleske 2013年

7
オブジェクトは、それが維持している一時ファイルを持っている場合も、それは明らかに、その後クリーンアップされません。
Mooing Duck 2013

47

C標準はmalloc、プログラムの終了時にによって割り当てられたメモリが解放されることを指定していません。これはオペレーティングシステムによって行われ、すべてのOS(通常、これらは組み込み環境にある)ではなく、プログラムの終了時にメモリを解放します。


20
それは、多かれ少なかれあるので、CのCを実行するために起こっている上のプログラムではなく、オペレーティング・システム...約C標準会談
vonbrand

5
@vonbrand C標準には、return mainによって割り当てられたすべてのメモリmallocが解放されると言うパラグラフがあるかもしれません。たとえば、プログラムが終了する前に、開いているすべてのファイルが閉じられると表示されます。my mallocに割り当てられたメモリについては、指定されていません。もちろん、OSに関する私の文章は、標準で規定されているものではなく、通常何が行われるかを説明しています。
ouah 2013年

私のコメントを訂正させてください。標準では、プログラムの起動と停止の方法ではなく、Cについて説明しています。OS なしで実行できる Cプログラムを作成することもできます。その場合、クリーンアップを行う人は誰もいません。標準は非常に必要とされない限り、意図的にそう必要としない用途を制限しないようにと、何も指定しません。
フォンブランド2013年

2
@ouah:「メインが戻るとき ...」それは仮定です。「メインが返ったら...」を考慮する必要があります。std::atexitはまた、によるプログラムの終了を考慮しstd::exit、さらにstd::abortand(C ++固有)もありstd::terminateます。
MSalters 2013年

@ouah:それが含まれていた場合、atexit使用できません。:-)
R .. GitHub ICEのヘルプを停止する

28

すべての回答は、最新のOSに関する質問のほとんどの側面をカバーしていますが、歴史的に、これまでDOSの世界でプログラミングしたことがある場合に言及する価値のあるものがあります。ターミナントアンドステイレジデント(TSR)プログラムは通常、システムに制御を返しますが、ソフトウェア/ハードウェアの割り込みによって回復できるメモリに常駐します。次のようなメッセージが表示されるのは正常ですこれらのOSで作業しているときに、「メモリ不足です!TSRの一部をアンロードしてみてください」です。

だから技術的には プログラムは終了しますプログラムはまだメモリ上にあるため、プログラムをアンロードしない限り、メモリリークは解放されません。

つまり、バグがあるか、組み込みOSがそうするように設計されているために、OSがメモリを再利用しないこととは別に、これを別のケースと考えることができます。

もう一つ例を覚えています。主にIBMメインフレームで実行されるトランザクションサーバーである顧客情報管理システム(CICS)は、疑似会話型です。実行されると、ユーザーが入力したデータを処理し、ユーザー用の別のデータセットを生成し、ユーザーターミナルノードに転送して終了します。アテンションキーをアクティブにすると、再び復活して別のデータセットを処理します。技術的には、その動作方法のため、CICSトランザクションサーバーをリサイクルしない限り、OSは終了したCICSプログラムからメモリを再利用しません。


それは本当に興味深いです、歴史的メモをありがとう!そのパラダイムが、メモリを解放する必要がなければ、計算コストが高すぎるためかどうかを知っていますか?または、代替案がまだ考えられていないだけでしたか?
DilithiumMatrix 2013年

1
@zhermes:DOSは単にTSRのメモリ割り当てを追跡しなかったため、計算上不可能でした。ほぼ定義上、目標は居住者維持することでした。TSRがすべてのメモリではなく一部のメモリを解放するようにしたい場合、何を解放するかを決めるのはあなた次第です。
MSalters 2013年

2
@zhermes:DOS(その前身であるCP / Mのような)は、現代の意味でオペレーティングシステムと呼ぶものではありませんでした。これは、実際には、一度に1つのプログラムを実行できるコマンドプロセッサにバンドルされた標準的な方法で呼び出すことができるI / Oユーティリティのコレクションにすぎません。プロセスの概念はなく、メモリは仮想でも保護でもありませんでした。TSRは、システムに最大64Kのスペースを使用していることを通知し、割り込みにフックして呼び出されるようにする有用なハックでした。
Blrfl 2013年

8

他の人が言ったように、ほとんどのオペレーティングシステムは、プロセスの終了時に割り当てられたメモリ(およびおそらくネットワークソケット、ファイルハンドルなどの他のリソース)を再利用します。

そうは言っても、(生のmalloc / freeの代わりに)new / deleteを処理するときに心配する必要があるのはメモリだけではないかもしれません。newに割り当てられたメモリは回収される場合がありますが、オブジェクトのデストラクタで行われる可能性のあることは起こりません。おそらく、あるクラスのデストラクタは、破壊時にセンチネル値をファイルに書き込みます。プロセスが終了するだけの場合、ファイルハンドルがフラッシュされてメモリが解放されることがありますが、そのセンチネル値は書き込まれません。

物語の教訓、常に自分の後を片付ける。物事をぶら下げないでください。OSのクリーンアップに依存しないでください。自分の後片付け。


'OSのクリーンアップに依存しないでください。自分の後を片付けなさい。」これはしばしば複雑です...複雑なマルチスレッドアプリでは「非常に、非常に困難」です。リソースへのすべての参照が失われた実際のリークは悪いです。明示的に参照を解放するのではなく、OSがクリーンアップできるようにすることは必ずしも悪いことではなく、多くの場合、唯一の妥当なコースをとります。
マーティンジェームズ

1
C ++では、プログラムの終了時にデストラクタ呼び出されます(あまり明るくないkill -9ファンが表示されない限り...)
vonbrand

@vonbrand True、ただし動的オブジェクトのリークについて話している場合、それらのデストラクタは発生しません。スコープ外のオブジェクトは生のポインタであり、そのデストラクタはノーオペレーションです。(もちろん、この問題を軽減するためにRAIIオブジェクトを参照してください...)
Andre Kostur 2013年

1
RAIIの問題は、プロセスの終了時にオブジェクトの割り当てを解除することを主張しているため、実際に取り除くことは重要ではないということです。注意が必要なDB接続ですが、一般的なメモリはOSによってクリーンアップされるのが最善です(はるかに優れた処理を行います)。この問題は、ページアウトされたメモリの量が増えると、絶対に経過して終了するプログラムとして現れます。また、解決するのは簡単ではありません…
ドナルフェロー

@vonbrand:それほど単純ではありません。キャッチされていない例外は、std::exitdtorsを呼び出しますstd::abort
MSalters 2013年

7

これは、言語よりもオペレーティングシステムに依存する可能性が高くなります。最終的に、どの言語のどのプログラムでも、オペレーティングシステムからメモリを取得します。

プログラムが終了/クラッシュしたときにメモリをリサイクルしないオペレーティングシステムについては聞いたことがありません。したがって、プログラムが割り当てる必要のあるメモリに上限がある場合は、割り当てるだけで解放しないことは完全に合理的です。


単純化したOSの場合、カーネルのメモリの画像を台無しにしてもらえませんか?..同様に、マルチタスクのないオペレーティングシステムです。
ulidtko 2013年

@ulidtko、これ物事を台無しにするでしょう。私のプログラムがたまに1GiBを必要とし、その間それをつかむ場合、それはそれを使用していないときでも他の人にそれらのリソースの使用を拒否しています。今日はそれが問題になるかもしれません。しかし、環境根本的変化します。保証されます。
フォンブランド2013年

@vonbrandまれな1GiBの使用は、通常の問題ではありません(十分な物理メモリがある限り)。現在のオペレーティングシステムは現在アクティブではないビットをページアウトできるためです。問題は、仮想メモリをホストするための物理メモリよりもアクティブに使用される仮想メモリが多い場合に発生します。
ドナルフェロー

5

プログラムが別のプログラムのアドレス空間に読み込まれる動的コンポーネント(「プラグイン」)に変換されると、きちんとしたメモリ管理を備えたオペレーティングシステムであっても、問題が発生します。能力の低いシステムに移植されるコードについて考える必要すらありません。

一方、すべてのメモリ解放すると、と、プログラムのクリーンアップのパフォーマンスに影響を与えるます。

私が取り組んでいる1つのプログラムでは、特定のテストケースがすべての動的メモリのグラフを再帰して1つずつ解放するため、プログラムが終了するまでに30秒以上必要でした。

妥当な解決策は、そこに機能を持たせてテストケースでカバーすることですが、アプリケーションを迅速に終了するために、プロダクションコードではオフにします。


5

タイトルに値するすべてのオペレーティングシステムは、終了後に行われたプロセスの混乱を解消します。しかし、常に予期しないイベントがあり、何らかの理由でアクセスが拒否され、一部の貧弱なプログラマーが可能性を予測しなかったため、少し後で再試行しない場合はどうなりますか?メモリリークがミッションクリティカルである場合は、常に自分をクリーンアップする方が常に安全です。それ以外の場合、IMOの作業にコストがかかる場合、IMOの価値はあまりありません。

編集:ループのように、メモリリークが蓄積する場所にある場合は、それらをクリーンアップする必要があります。私が話すメモリリークは、プログラムの経過を通じて一定の時間内に蓄積されるものです。他の種類のリークがある場合は、遅かれ早かれ深刻な問題になる可能性があります。

技術的には、リークがメモリの「複雑さ」O(1)である場合、ほとんどの場合問題ありません。O(logn)はすでに不快で(場合によっては致命的)、O(N)+は耐えられません。


3

POSIX準拠システムの共有メモリは、shm_unlinkが呼び出されるか、システムが再起動されるまで持続します。


2

プロセス間通信がある場合、プロトコルによっては、他のプロセスがリソースを完了および消費しないことがあります。

例を挙げれば、プリンタジョブの途中でJVMを終了したときに、JavaでPDFプリンタへの印刷を試していたところ、PDFスプールプロセスがアクティブのままであり、タスクマネージャーでそれを強制終了しないと、印刷を再試行してください。

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