ヒープ内にリンクリストを作成する架空のプログラムを考えてみましょう。プログラムの最後に、すべてのノードを解放して終了するループがあります。この場合、リンクされたリストはわずか500Kのメモリであり、特別なスペース管理は必要ありません。
私は主にUNIXベースのシステムに興味がありますが、どんな情報でも大歓迎です。今日はOSコースの最初のレッスンを受講しましたが、今はそれについて疑問に思っています。
編集:多くの人が副作用や一般的な「プログラミングの実践」などを懸念しているため。あなたが正しいです!私はあなたの発言に100%同意します。しかし、私の質問は仮説にすぎません。OSがこれをどのように管理しているかを知りたいのです。したがって、「すべてのメモリを解放するときに他のバグを見つける」などは省略してください。
わかりました。インストラクターからメールが届きました。非常に適切で合理的な回答が含まれています。ここにあります:
こんにちはラムジ、
あなたが始めた興味深いメッセージスレッドへのリンクをありがとう。
私たちの議論によると、mallocはプロセスの仮想メモリ上で動作します。そのため、プロセスが停止すると、その仮想アドレス空間が「消え」、それにマップされていた物理メモリが解放されます。したがって、「優れたソフトウェアエンジニアリング手法」などを無視すると、プロセスを終了する直前の動的メモリの割り当て解除は、時間の無駄になります。
(言うまでもなく、これは単一のスレッドが終了しても、そのプロセスの他のスレッドが実行を続ける場合には当てはまりません。)
だからこれによれば:
あなたのアプローチの私の主な問題は、リーク検出ツール(Valgrindなど)がそれを報告し、それを無視し始めることです。その後、いつの日か実際のリークが発生する可能性があり、すべてのノイズのために気付くことはありません。
かつて、動的に割り当てられた両端キューを使用してアルゴリズムを実装する必要がありました。また、出口で割り当てられたすべてのデータの割り当てを解除する必要があるかどうかも疑問に思っていました。
とにかく、割り当て解除を実装することを決定したところ、割り当て解除中にプログラムがクラッシュしたことがわかりました。クラッシュを分析したところ、メインデータ構造とアルゴリズムの実装にエラー(メモリリーク)が見つかりました。
私が学んだ教訓は:
ちょうど私の2セント。
[〜#〜]編集[〜#〜]
小さな説明:もちろん、プロセスに割り当てられたすべてのメモリはプロセスが終了すると解放されます。したがって、割り当てられたメモリを解放することが唯一の要件である場合、明示的にそれを行うことは確かに時間の無駄です。
常にリソースを解放します。すべてのリソースを解放することは、リークを効果的に検出できる唯一の方法であるため、重要です。私が取り組んでいるプロジェクトでは、カナリアの挿入、統計の追跡などのために、malloc/realloc/freeのラッパーも用意しています。単に問題をより早く検出するためです。
ただし、リーク検出を除いて、(以前ではなく)出口で解放されるリソースの場合は、時間の無駄です。 OSはそれを解放できなければなりません(たとえば、プロセスがクラッシュした場合、またはRAMを解放しただけでも));そしてOSはあなたよりも速く解放できるはずですカーネルは「断片的」ではなく「卸売り」を行うため、ユーザー空間から実行でき、カーネルとプロセス間の移行が少なくなります。
幸いなことに、何らかの方法で選択する必要はありません。コードが正しいことを確認したいが(そうである場合)、可能な限り最高のパフォーマンスが必要な場合は、次のようにします。
#ifndef FAST_EXIT
free(stuff);
#endif
クリーンアップルーチンは、定期的にスペースを取り戻すために実行したり、割り当てた数のノードを正確に再利用できることを証明したり、メモリリークがないことを確認したりする場合に役立ちます。しかし、プロセスが終了する直前のハウスキーピングでは、OSがその瞬間にメモリの領域全体の制御を取り戻すため、意味がありません。
後で別の動作がありますか?
あなたが尋ねる必要があるこれに関連する重要な質問があります:
誰かがこのコードを何らかの理由で再び使用することはありますか?
答えが「はい」になる可能性がある場合、コードが単独で使用される場合でも、大規模なシステムの一部として使用される場合でも、次の人が踏むための地雷を漏らす記念碑的なメモリを作成しました。
他の人が使用するソフトウェアを含む将来のあなた(だけではなく)を作成する方法を学習していることを、授業中に覚えることは非常に困難ですその日の宿題の問題を解決します)。これは、次のような特定の基本的なプログラミング動作を順守する必要があることを意味します。
独自のデータ型「リンクリスト」を実装する場合、クリーンアップルーチン/デコンストラクターを作成しないと、予想以上に長期的に頭痛の種になるでしょう。
クリーンアップコードなしで記述したものを再利用しないと、特別な注意を払わなければ型を使用できないため(たとえば、関数やループでそれらを作成することはできませんが、main
でのみ作成することをお勧めします)。
自分自身と他の人に好意を示し、あなたに書いてくださいlist_cleanup
または~List
。
マルチユーザーシステムでは、OSは常にそのようなリソースを解放します。解放しないとセキュリティリスクになるためです。あなたの記憶内容は他の誰かのプロセスで終わるかもしれません!
クリーンアップルーチンには、他のコードと同じようにバグが含まれる場合があります。悲しい光景は、終了時にハングまたはクラッシュすることを除いて、あなたが望むことをするプログラムです。一般的な方法は、コードを自動的にクリーンアップすることですが、プロセスの終了時にメモリをクリーンアップする必要がない場合もあります。
ほとんど常にはい。無料の共有メモリセマフォとUDPサーバーリソース(OSは、接続されたUDPサーバーに完了を通知する方法を知りません)。
私は常に通常の終了時にリソースをクリーンアップします。リソースの割り当て/割り当て解除の呼び出しが正しく行われているのは安全チェックです。このチェックにより、隠れたバグが2回以上警告されました。それは一日の終わりにビジネスの本のバランスを取るようなものです。
コメントに@MSalterなどのEdgeケースが記載されている場合もありますが、それでも、OSをクリーンアップして最高のものを期待するだけでなく、通常はカスタマイズされたメモリ管理を検討し始めます。
他のプロセスがリンクリストとその中のノードを参照していないことを100%確信している場合は、OKをクリックします。
ただし、100%はかなり高い数値です。あなたのリストはプールされているいくつかの永続化コードによって参照されましたか? UIコンポーネントにはまだハンドルがありますか? 何か他に?
通常、メモリリークは意図的に発生するものではありません。
問題は、今日はリンクリストノードであり、明日はOSだけではクリーンアップできないリソースであることです。ヒープメモリをクリーンアップするためにOSを離れるのは速くて安全ですか?はい。しかし、それはそれを良いアイデアにしていますか?必ずしも。