メモリリークは大丈夫ですか?[閉まっている]


231

CまたはC ++アプリケーションでメモリリークが発生しても許容されますか?

アプリケーションのコードの最後の行(たとえば、グローバルオブジェクトのデストラクタ)までメモリを割り当てて使用するとどうなるでしょうか。メモリ使用量が時間の経過とともに増加しない限り、アプリケーションが終了したときに(Windows、Mac、およびLinux)OSを信頼してメモリを解放してもよいですか?OSによって解放されるまでメモリが継続的に使用されていた場合、これを実際のメモリリークと見なしますか?

サードパーティのライブラリがあなたにこの状況を強いた場合はどうなりますか?それがどれほど優れていても、サードパーティのライブラリの使用を拒否しますか?

実用上の欠点は1つだけです。つまり、これらの良性のリークは、メモリリーク検出ツールで誤検知として表示されます。


50
メモリ使用量が時間の経過とともに増加しない場合、それはリークではありません。
mpez0

4
ほとんどのアプリケーション(すべての.NETプログラムを含む)には、一度割り当てられ、明示的に解放されない少なくともいくつかのバッファーがあるため、mpez0の定義の方が便利です。
Ben Voigt

2
はい、無限の記憶があれば。
ユーザー

「良性」のリーク(そのようなものがある場合)は誤検知ではありません-非常に正確に検出されたリークです。個人的に修正したくないリークであっても、リーク検出はリーク検出器の存在理由です。
cHao

1
@ mpez0「メモリ使用量が時間とともに増加しない場合、それはリークではありません」?それはメモリリークの定義ではありません。リークとは、リークされたメモリのことです。つまり、解放されておらず、参照できなくなっているため、再び解放することはできません。成長するかどうかは関係ありません。
Mecki

回答:


329

番号。

専門家として、私たちが自問してはならない質問は、「これを実行しても大丈夫ですか?」です。むしろ「これを行う正当な理由はありますか?」そして、「メモリリークを突き止めるのは難しい」というのは正当な理由ではない。

私は物事をシンプルに保つのが好きです。そして単純なルールは、私のプログラムにはメモリリークがあってはならないということです。

それも私の人生をシンプルにします。メモリリークを検出した場合は、精巧なデシジョンツリー構造を実行して「許容可能な」メモリリークかどうかを判断するのではなく、メモリリークを排除します。

これはコンパイラの警告に似ています–警告は私の特定のアプリケーションにとって致命的ですか?そうでないかもしれない。

しかし、それは最終的には専門家の規律の問題です。コンパイラの警告を許容し、メモリリークを許容することは悪い習慣であり、最終的には私を後ろから噛みます。

極端なことを言うと、外科医が手術器具を患者の体内に残しておいても容認できるでしょうか。

SurgeonOverflow.comに投稿されたこの質問を見れば、その機器の取り外しのコスト/リスクがそれを残すことのコスト/リスクを超える状況が発生する可能性があり、それが無害な状況である可能性があります「いいえ」以外の答えを見つけた場合、それは医療専門家に対する私の自信を大きく損なうことになります。

サードパーティの図書館が私にこの状況を強いた場合、それは問題の図書館の全体的な質を真剣に疑うようになるでしょう。それはまるで私が車を運転してテストし、カップホルダーの1つにいくつかの緩いワッシャーとナットを見つけたかのようになります。それ自体は大した問題ではないかもしれませんが、品質への取り組みの欠如を描いているため、代替案を検討します。


57
同時に正しいと正しくない。私たちの究極のほとんどは賃金奴隷であり、職人技への欲求はビジネスの要件に後退する必要があります。そのサードパーティライブラリにリークがあり、2週間の作業が節約できる場合、それを使用するビジネスケースなどがあるかもしれません...
Cervo

3
とにかく私が必要とするもので、適切な代替手段がない場合は、ライブラリを使用しますが、メンテナにバグを記録します。
tloach 2008

7
個人的にはまったく同じ答えで答えますが、メモリをほとんど解放しないプログラムもあります。その理由は、a)メモリを解放するOSで実行することを目的としており、b)長時間実行しないように設計されているためです。確かにプログラムのまれな制約ですが、私はこれを完全に有効なものとして受け入れます。

2
早期チェックの理由をいくつか追加します。デバッグツールが「良性」のリークで溢れている場合、「実際の」リークをどのように見つけますか?バッチ機能を追加すると、突然1K /時間のリークが1K /秒になりますか?
peterchen 2009

5
うーん、「メモリリークしない」「完璧」ですか?
JohnMcG

80

「使用」されているメモリの量が増え続けない限り、メモリリークとは見なしません。未リリースのメモリがあることは理想的ではありませんが、必要なメモリ量が増え続けない限り、大きな問題にはなりません。


12
技術的には、リークは割り当てられたメモリであり、それへのすべての参照が失われます。最後にメモリの割り当てを解除しないのは単に面倒です。
マーティンヨーク

17
4 GBの1回限りのメモリリークがある場合、それは問題です。
John Dibling 2008年

21
成長しているかどうかは関係ありません。割り当てられている場合、他のプログラムはメモリを使用できません。
リザードに請求する

8
>割り当てられている場合、他のプログラムはメモリを使用できません。まあ、OSは常にメモリをディスクにスワップし、他のアプリケーションが利用していないRAMを使用できるようにします。
Max Lybbert 2008年

4
プログラムの寿命が非常に短い場合、リークはそれほど悪いものではない可能性があります。また、ページングは​​理想的ではありませんが、プログラムはそのメモリに関心がないため(したがって、常にスワップしているわけではないため)、ページングは​​ここにあるものほど高価ではありません-もちろん、 GC ...
Arafangion 2009年

79

まず、定義を正確にしましょう。メモリリークは、メモリが動的に割り当てられた場合(例:)malloc()に対応し、メモリへのすべての参照が対応する空き領域なしで失われます。作成する簡単な方法は次のとおりです。

#define BLK ((size_t)1024)
while(1){
    void * vp = malloc(BLK);
}

while(1)ループを回るたびに、1024(+ overhead)バイトが割り当てられ、新しいアドレスがvpに割り当てられることに注意してください。以前にmallocされたブロックへのポインタは残っていません。このプログラムは、ヒープがなくなるまで実行されることが保証されており、mallocされたメモリを回復する方法はありません。メモリはヒープから「漏れ」、二度と見られません。

しかし、あなたが説明しているものは

int main(){
    void * vp = malloc(LOTS);
    // Go do something useful
    return 0;
}

メモリを割り当て、プログラムが終了するまでそれを使用します。これはメモリリークではありません。プログラムを損なうことはなく、プログラムの終了時にすべてのメモリが自動的に消去されます。

通常、メモリリークを回避する必要があります。まず、あなたの上の高度や格納庫で燃料を補給するのと同じように、漏れて回復できないメモリは役に立たない。2つ目は、後でメモリリークを見つけるよりも、最初にメモリリークを起こさずに正しくコーディングする方がはるかに簡単です。


次に、この割り当ての数十を検討します。ここで、「メイン」本体を、複数回呼び出されるルーチンに移動する必要があることを検討してください。楽しい。-このシナリオでは、これは非常に大きな問題ではないという意見に同意しますが、シナリオは変化します。彼らが言うように、常にそれを維持する人があなたがどこに住んでいるか知っているかのようにコードを書きなさい。
peterchen 2009

2
まあ、ポイントは、mallocされ、プログラムが_exit()を呼び出すまで保持されるメモリが「リーク」されないことです。
チャーリーマーティン

1
これはメモリリークであり、プログラムを損なう可能性があります。mallocがすべての場所で非nilを返したことを確認しているので、今後の割り当てはこのプロセスから失敗する可能性があります。メモリが不足している埋め込み型の状況などで、メモリを使いすぎると、これが生と死の違いになる可能性があります。
MikeJ

10
マイク、それは真実ではありません。準拠C環境では、mainを終了するとすべてのプロセスリソースが解放されます。あなたが説明しているような組み込み環境では、そのような状況が見られるかもしれませんが、メインはありません。ここで、これが当てはまらない欠陥のある組み込み環境が存在する可能性があることを認めますが、+ =を正しく処理できない欠陥のある環境も確認しました。
チャーリーマーティン

3
はい、mallocメモリが多すぎるとそれは悪いことであることがわかりました。それでもリークではありません。参照が失われるdメモリでない限り、それはリークではありませんmalloc
チャーリーマーティン

39

理論的には、いいえ、実際には異なります。

それは、プログラムが作業しているデータの量、プログラムが実行される頻度、およびプログラムが常に実行されているかどうかに本当に依存します。

少量のデータを読み取る簡単なプログラムで計算を行って終了すると、小さなメモリリークに気付くことはありません。プログラムが長時間実行されておらず、メモリの使用量が少ないため、プログラムが存在する場合、リークは小さく、解放されます。

一方、何百万ものレコードを処理して長時間実行するプログラムがある場合、小さなメモリリークにより、十分な時間が与えられたマシンがダウンする可能性があります。

リークのあるサードパーティのライブラリについては、それらが問題を引き起こす場合は、ライブラリを修正するか、より良い代替案を見つけてください。それが問題を引き起こさないのであれば、それは本当に重要ですか?


質問全体を読んだかどうかはわかりません。メモリはアプリケーションの最後まで使用されると言っています。時間とともに成長しません。唯一の違いは、解放/削除の呼び出しがないことです。
Imbue 2008年

2
その場合、それは実際にはメモリリークではありません。メモリリークは、使用されていないが解放されていない少量のメモリです。この量は時間の経過とともに大きくなります。あなたが話しているのは記憶の小滴です。液滴が非常に大きい場合を除いて、これについて気にしないでください。
vfilby 2008年

「それが問題を引き起こさないのなら、それは本当に重要ですか?」いいえ、まったく問題ありません。信心深くなる代わりに、もっと多くの人がそれを手に入れたいと思います。
Imbue 2008年

2
@ジョン:それは一般に怠惰な開発者の問題ではなく、進化するソフトウェアの問題です。私たちは皆間違いを犯し、バグは私たちの仕事です。私たちはそれらを修正し、それを修正します。それが私たちが行うことです。それは常に初期費用と長期保守のバランスであり、そのバランスは決して簡単ではありません。
vfilby 2008年

1
ジョン、私は100%あなたに同意します。Sloppy is sloppy ..モニターの後ろにエビを置いておきます。悪臭は悪臭です。私たちが洞窟を掘るたびに、私たちの業界は少し洞窟になります。リークがあることがわかっていて、それが原因であることがわかっている場合は、修正する必要があります。
baash05 2009年

37

多くの人は、メモリを解放するとすぐにオペレーティングシステムに戻り、他のプログラムで使用できるという印象を受けているようです。

これは真実ではありません。通常、オペレーティングシステムは4KiBページでメモリを管理します。mallocその他の種類のメモリ管理は、OSからページを取得し、適切と思われるときにサブ管理します。プログラムが後でより多くのメモリをmallocすることを前提として、オペレーティングシステムにページを返さない可能性free()あります。

free()メモリをオペレーティングシステムに戻さないと言っているのではありません。これは、特に大量のメモリを解放している場合に発生する可能性があります。しかし、保証はありません。

重要な事実:不要になったメモリを解放しない場合、さらにmallocsがメモリを消費することが保証されます。ただし、最初に解放すると、代わりにmallocが解放されたメモリを再利用する可能性があります。

これは実際にはどういう意味ですか?つまり、プログラムが今後メモリを必要としないことがわかっている場合(たとえば、クリーンアップ段階にある場合)、メモリを解放することはそれほど重要ではありません。ただし、プログラムが後でより多くのメモリを割り当てる可能性がある場合は、メモリリーク、特に繰り返し発生する可能性のあるメモリリークを回避する必要があります。

また、終了直前にメモリを解放することが悪い理由の詳細については、このコメントを参照してください。

コメンターは、呼び出しfree()が自動的に他のプログラムが解放されたメモリを使用することを許可しないことを理解していないようです。しかし、これがこの回答の要点です。

そこで、人々を納得させるために、free()がほとんど役に立たない例を示します。数学をわかりやすくするために、OSが4000バイトのページでメモリを管理していると仮定します。

100バイトのブロックを1万個割り当てたとします(簡単にするために、これらの割り当てを管理するために必要な余分なメモリは無視します)。これは1MB、つまり250ページを消費します。その後、これらのブロックのうち9000個をランダムに解放すると、1000個のブロックしか残りませんが、それらはあちこちに散らばっています。統計的には、約5ページが空になります。他の245にはそれぞれ、少なくとも1つの割り当てられたブロックがあります。これは980KBのメモリに相当し、100KBしか割り当てられていなくても、オペレーティングシステムで回収することはできません。

一方、プログラムが占有しているメモリの量を増やすことなく、malloc()をさらに9000ブロック実行できるようになりました。

技術的にOSにメモリを戻すfree()ことができたとしても、それができない場合があります。迅速な操作とメモリの節約のバランスをとる必要があります。さらに、すでに大量のメモリを割り当ててから解放したプログラムは、再度割り当てを行う可能性があります。Webサーバーは、リクエストごとにリクエストを処理する必要があります。OSに常にメモリを要求する必要がないように、「スラック」メモリを使用可能にしておくことは理にかなっています。free()


1
他のプログラムがプログラムが不必要に保持しているメモリを必要とする場合、それ以上mallocsは必要ないかもしれませんが、未使用のメモリ領域をfree()します:)
MN

2
あなたは私のポイントを完全に逃しました。メモリを解放()すると、他のプログラムはそれを使用できなくなります!! (特に、メモリの大きなブロックを解放する場合は、可能です。しかし、できない場合が多いです!)投稿を編集して、これをより明確にします。
Artelius 2009年

27

アプリケーションの実行後にOSをクリーンアップしても、概念的には何も問題はありません。

それは実際にはアプリケーションとその実行方法に依存します。何週間も実行する必要のあるアプリケーションで継続的に発生するリークに対処する必要がありますが、メモリをあまり必要とせずに結果を計算する小さなツールは問題になりません。

多くのスクリプト言語が循環参照のガベージコレクションを行わないのには理由があります。それらの使用パターンについては、これは実際の問題ではないため、メモリの浪費と同じくらいリソースの無駄になります。


スクリプト言語について:Pythonは参照カウントを使用しますが、循環参照を解放するためだけにGCを備えています。他の言語では、プログラマーは明示的な循環参照を完全に回避することがよくあり、他の問題を引き起こします。
Blaisorblade、2009年

以前のバージョンのPHPはメモリを解放しませんでした。メモリは最初から最後まで実行され、通常は0.1秒の実行時間後にスクリプトが終了し、すべてのメモリが解放されました。
アラファンギオン2009年

19

答えはノーだと思います。決してメモリリークを許可しないでください。明示的に述べていない理由がいくつかあります。ここには素晴らしい技術的な答えがありますが、実際の答えは、より社会的/人間的な理由にかかっていると思います。

(最初に、他の人が述べたように、真のリークは、プログラムがいつでも、割り当てたメモリリソースを追跡できなくなることです。Cでは、これは malloc()なく、ポインターに移動し、そのポインターがスコープを離れると、free()最初。)

ここでのあなたの決定の重要な核心は習慣です。用途ポインタは、あなたがポインタを使用するつもりだという言語でするときは、コードの多くを。そしてポインタは危険です。これらは、あらゆる種類の深刻な問題をコードに追加する最も簡単な方法です。

コーディングをしていると、ボールに乗ったり、疲れたり、怒ったり、心配したりすることがあります。これらのやや気が散る時間の間、あなたは自動操縦でより多くのコーディングをしています。自動操縦効果は、1回限りのコードと大きなプロジェクトのモジュールを区別しません。それらの時間の間、あなたが確立する習慣はあなたのコードベースで最終的に何になるかです。

そのため、現時点で道路上にいる唯一の車であっても、車線変更時に死角を確認する必要があるのと同じ理由で、メモリリークを許可しないでください。 あなたの活動的な脳が気を散らされている時、良い習慣はあなたを悲惨な失敗からあなたを救うことができるすべてです。

「習慣」の問題を超えて、ポインターは複雑であり、多くの場合、精神的に追跡するには多くの頭脳の力が必要です。ポインターの使用に関しては、特にプログラミングに不慣れな場合は、「水を濁らせない」ことが最善です。

より社会的な側面もあります。malloc()およびを適切に使用free()することで、コードを見る人は誰でも安心することができます。リソースを管理しています。ただし、そうしないと、すぐに問題が疑われます。

たぶん、メモリリークがこのコンテキストで何も害を及ぼさないことがわかったかもしれませんが、コードのすべてのメンテナーは、そのコードを読むときに頭の中でそれを解決する必要があります。を使用してfree()することで、問題を考慮する必要もなくなります。

最後に、プログラミングとは、プロセスのメンタルモデルを明確な言語で記述し、人とコンピュータがそのプロセスを完全に理解できるようにすることです。優れたプログラミング実践の重要な部分は、不必要な曖昧さをもたらすことはありません。

スマートプログラミングは柔軟で汎用的です。悪いプログラミングはあいまいです。


私は習慣のアイデアが大好きです。私も同意します。メモリリークが発生した場合、コーダーが他のコーナーを切り落としたのではないかといつも思っています。特に明白な場合
baash05

これは断然最良の答えです。私はC ++で5年間プログラミングしていて、メモリリークを1つも書いたことがありません。その理由は、メモリリークが発生しやすいコードを記述していないためです。優れたC ++設計ではめったに使用しないためnew、ほとんどのメモリリークをすぐに排除できます。どうしても必要な場合にのみ使用してくださいnew。その結果はnewすぐにスマートポインターに配置する必要があります。これらの2つのルールに従えば、メモリをリークすることは決してありません(ライブラリのバグがなければ)。残っている唯一のケースはshared_ptrサイクルですweak_ptr。その場合、使用することを知っている必要があります。
David Stone

15

あなたの状況では、答えは大丈夫だと思うかもしれません。ただし、メモリリークは意識的な決定であることを文書化する必要があります。メンテナンスプログラマーがやって来て、関数内でコードを平手打ちして、100万回呼び出してほしくない。したがって、リークが大丈夫であると決定した場合は、将来プログラムに取り組む必要がある人のために、それを(大きな文字で)文書化する必要があります。

これがサードパーティのライブラリである場合、閉じ込められる可能性があります。ただし、このリークが発生したことを明確に文書化してください。

しかし、基本的に、メモリリークが512 KBバッファなどの既知の量である場合、それは問題ではありません。ライブラリコールを呼び出すたびにメモリリークが増加し続ける場合、メモリが512KB増加し、解放されない場合は、問題がある可能性があります。あなたがそれを文書化し、呼び出しが実行される回数を制御する場合、それは扱いやすいかもしれません。ただし、512はそれほど多くありませんが、512を超える呼び出しは512を超えるため、ドキュメントが本当に必要です。

また、オペレーティングシステムのドキュメントを確認する必要があります。これが組み込みデバイスの場合、終了するプログラムからすべてのメモリを解放しないオペレーティングシステムが存在する可能性があります。よくわかりませんが、これは正しくないかもしれません。しかし、調査する価値があります。


3
「しかし、メモリリークは意識的な決定であることを文書化する必要があります。」天に感謝します。これまでに作った最高のポイント。
害虫駆除、2008年

15

プログラムのメモリ使用量を減らすことができない限り、メモリを解放することは常に間違っているという不人気ですが実際的な答えを提供します。たとえば、単一の割り当てまたは一連の割り当てを行って、存続期間全体で使用するデータセットをロードするプログラムは、何も解放する必要はありません。非常に動的なメモリ要件を持つ大規模なプログラムのより一般的なケース(Webブラウザーを考えてみてください)では、使用していないメモリをできるだけ早く解放する必要があります(たとえば、タブ/ドキュメントを閉じるなど)。ですが、ユーザーが「exit」をクリックしたときに何も解放する必要はありません。解放すると、ユーザーエクスペリエンスに悪影響を及ぼします。

どうして?メモリを解放するには、メモリに触れる必要があります。システムのmalloc実装が割り当てられたメモリブロックに隣接するメタデータを格納しない場合でも、解放する必要のあるすべてのポインターを見つけるために再帰的な構造を歩くことになります。

ここで、プログラムが大量のデータを処理したものの、しばらくの間ほとんどそのデータに触れなかったとします(ここでも、Webブラウザーが良い例です)。ユーザーが多くのアプリを実行している場合、そのデータのかなりの部分がディスクにスワップされている可能性があります。単にexit(0)またはメインから戻ると、すぐに終了します。優れたユーザーエクスペリエンス。すべてを解放しようとする問題が発生した場合は、5秒以上かけてすべてのデータをスワップインし直し、その直後に破棄するだけです。ユーザーの時間の無駄。ラップトップのバッテリー寿命の無駄。ハードディスクの摩耗の無駄。

これは単なる理論的なものではありません。ロードしたアプリが多すぎてディスクがスラッシングし始めたときはいつでも、「終了」をクリックすることすら考えていません。私はできるだけ早く端末に行き、killall -9と入力します...「exit」はそれを悪化させるだけなので知っています。


5
レイモンドチェンの次の言葉が大好きです。「建物は取り壊されています。床を掃除したり、ゴミ箱を空にしたり、ホワイトボードを消したりしないでください。そして、建物の出口に並んで、誰もが建物内に移動できるようにしないでください。あなたがしているのは、解体チームにあなたがこれらの無意味なハウスクリーニング作業を完了するのを待つことです。」(blogs.msdn.microsoft.com/oldnewthing/20120105-00/?p=8683
アンドレアスMagnussonの

11

誰かが「はい」と言う理由を考え出すことができると確信していますが、それは私ではありません。「いいえ」ではなく、「はい/いいえ」の質問であってはなりません。メモリリークを管理または封じ込める方法はいくつかあり、多くのシステムにはそれらがあります。

これを計画している地球を去るデバイスのNASAシステムがあります。システムは時々自動的に再起動するため、メモリリークが全体的な操作に致命的になることはありません。封じ込めの一例です。


これは実際にはソフトウェアの老朽化の一例です。魅力的な研究テーマ。
Konrad Rudolph

たまに自動再起動しますよね?NASA、ハァッ?(*古いMicrosoft WindowsインストールCDを見る*)これは非常によく
わかり

8

メモリを割り当てて、プログラムの最終行までそれを使用する場合、それはリークではありません。メモリを割り当ててそれを忘れると、たとえメモリの量が増えていなくても、それは問題です。割り当てられているが未使用のメモリがあると、他のプログラムの実行速度が低下したり、まったく実行されなくなったりする可能性があります。


使用されていない場合は、ページアウトされるだけなので、実際にはそうではありません。アプリが終了すると、すべてのメモリが解放されます。
Eclipseの

割り当てられている限り、他のプログラムはそれを使用できません。割り当てを解除しないと、ページアウトされません。
トカゲに請求する

もちろんそれは可能です-それが仮想メモリのすべてです。実際のRAMは1 GBですが、4つのプロセスでそれぞれ2 GBの仮想メモリを完全に割り当てることができます(ページファイルが十分な大きさである限り)。
Eclipseの

もちろん、それらのプロセスのそれぞれがそのすべてのメモリをアクティブに使用している場合、厄介なページングの問題が発生します。
Eclipseの

さて、私はあなたが今話していることを理解しています。使用していないメモリの割り当てを解除すると、ページングの必要性が減ります。あなたはそれが割り当てられ続けるならば、それはページインバックだとき、あなたのアプリケーションがまだそれを維持します。
トカゲのビル

8

一方では、時間の経過とともに見た「良性」のリークの数を数えることができます。

だから答えは非常に有資格です。

例。循環キューまたは両端キューを格納するためのバッファーを必要とするシングルトンリソースがあるが、バッファーの大きさがどれほど必要かわからず、ロックまたはすべてのリーダーのオーバーヘッドを許容できない場合は、指数倍増バッファーを割り当てますが、古いものを解放しないと、キュー/デックごとに制限された量のメモリがリークします。これらの利点は、すべてのアクセスを劇的にスピードアップし、ロックの競合を危険にさらすことなくマルチプロセッサソリューションの漸近性を変えることができることです。

このアプローチは、CPUごとのワークスティーリングの両端キューなど、非常に明確に固定されたカウントのあるものに大きな利益をもたらし、/proc/self/mapsHans Boehmの保守的なガベージコレクターのCのシングルトン状態を保持するために使用されるバッファーでははるかに少ない程度に使用されます。/ C ++、ルートセットなどを検出するために使用されます。

技術的に漏れ、これらのケースの両方が、サイズおよび両端キューケースを盗む可変長円形のワークに囲まれている間に存在する巨大なキューのためのメモリ使用量が2増加の有界因子と引き換えに性能勝利は。


1
ハザードポインターを使用して、漏れを防ぐことができます。
デミ

8

プログラムの最初に大量のヒープを割り当て、終了時に解放しない場合、それ自体はメモリリークではありません。メモリリークは、プログラムがコードのセクションをループし、そのコードがヒープを割り当てた後、解放せずに「トラックを失う」場合です。

実際、終了する直前にfree()やdeleteを呼び出す必要はありません。プロセスが終了すると、そのメモリはすべてOSによって回収されます(これは確かにPOSIXの場合です。他のOS(特に組み込みOS)ではYMMV)。

終了時にメモリを解放しない場合の唯一の注意点は、プログラムをリファクタリングして、たとえば、入力を待機するサービスになり、プログラムが行うことをすべて実行し、ループして待機することです。別のサービスコールは、その後、あなたがコード化されたものができ、メモリリークに変わります。


失礼ですが同意できません。それ「メモリリークそのもの」です。
Konrad Rudolph、

オブジェクトへの参照を「失う」までは、リークではありません。おそらく、プログラムの存続期間にわたってメモリが使用されている場合、メモリはリークされません。exit()が呼び出されるまで参照が失われない場合、それは絶対にリークではありません
nsayer 2008年

Amiga DOSは、プロセスの背後をクリーンアップしない最後のO / SIでした。ただし、プロセスが使用していない場合でも、System V IPC共有メモリが残っている可能性があることに注意してください。
ジョナサンレフラー、

Palmは、hotsyncするまで「リーク」したメモリを解放しません。それはアミガの後によくやって来ました。パームエミュレータでリークが発生したアプリを実行しました。実際のパームに到達することはありませんでした。
baash05 2009年

6

これはドメイン固有なので、答える価値はほとんどありません。あなたのひどい頭を使用してください。

  • スペースシャトルのオペレーティングシステム:いいえ、メモリリークは許可されていません
  • 迅速な開発の概念実証コード:これらすべてのメモリリークの修正は時間の無駄です。

そして、中間的な状況のスペクトルがあります。

最悪のメモリリークを除くすべてを修正するために製品のリリースを遅らせる機会費用($$$)は、通常、「ずさんな、または専門家ではない」という気持ちを小さくします。あなたの上司は彼にお金を稼ぐためにあなたにお金を払います。


2
非常に近視眼的態度。基本的に、プログラミングの欠陥が原因であることが判明するまで、根本的に適切なプログラミング方法を使用する必要はないと言っています。問題は、ずさんな方法で書かれたソフトウェアは、そうでないソフトウェアよりも多くの欠陥を持つ傾向があるということです。
John Dibling 2008年

1
私はそのすべてを信じていません。そして、メモリ管理はクリーンなメソッドを書くよりも複雑です。
ダスティンゲッツ

1
ダスティンは明らかに私たちのほとんどと同じように現実の世界で機能します。そこでは、競争に追いつくために狂った締め切りに絶えず取り組んでいます。そのため、バグへの対処は実用的な方法で行う必要があります。重要でないプログラムの重要でないバグに多くの時間を費やすことによって、あなたは自分の仕事を終わらせないでしょう。
Wouter van Nifterick、2009年

この態度の問題は次のとおりです。いつリークを修正し始めますか?「OK、それは発電所ですが、それはウランではなく石炭です。なぜここでリークを修正するのですか?」-現実の世界で、最初から正しいことをしないと、いつもそれが起こることはないということを学びました。その姿勢は、2週間後に「99%完了」し、2か月間続くプロジェクトを生み出します。
peterchen 2009

5

最初に、認識されたメモリリークと実際のメモリリークには大きな違いがあることを理解する必要があります。非常に頻繁に分析ツールは多くの赤いニシンを報告し、リークされたもの(メモリまたはハンドルなどのリソース)に実際にはリークされていないものとしてラベルを付けます。多くの場合、これは分析ツールのアーキテクチャが原因です。たとえば、特定の分析ツールは、実行時オブジェクトが解放されたことを決して認識しないため、ランタイムオブジェクトをメモリリークとして報告します。ただし、割り当て解除はランタイムのシャットダウンコードで行われるため、分析ツールでは確認できない場合があります。

そうは言っても、見つけるのが非常に難しいか、修正するのが非常に難しい実際のメモリリークが発生する場合があります。だから今問題はコードにそれらを残してもいいですか?

理想的な答えは、「いいえ、絶対にしない」です。より実用的な答えは「いいえ、ほとんどない」かもしれません。現実の世界では、解決するリソースと時間に限りがあり、タスクのリストが無限にあることがよくあります。タスクの1つがメモリリークを解消している場合、リターンを減少させる法則が頻繁に作用します。1週間でアプリケーションのすべてのメモリリークの98%を排除できますが、残りの2%には数か月かかる可能性があります。場合によっては、コードの大幅なリファクタリングを行わないアプリケーションのアーキテクチャが原因で、特定のリークを排除することさえ不可能かもしれません。残りの2%を排除することのコストと利点を比較検討する必要があります。


5

この種の質問には、コンテキストがすべてです。個人的に私はリークに耐えることができません、そして私のコードでは、それらが発生した場合、それらを修正するためにかなりの時間をかけますが、リークを修正することは常に価値があるとは限りません、そして人々が私が時々持っている時間までに私にお金を払っているとき彼らのコードのリークを修正するのは私の手数料の価値がないと彼らに言いました。例を挙げましょう。

私はプロジェクトをトリアージし、いくつかのパフォーマンス作業を行い、多くのバグを修正していました。アプリケーションの初期化中に、追跡して完全に理解したリークがありました。それを適切に修正するには、1日程度で機能するコードをリファクタリングする必要がありました。私は何かハッキーなことをしたかもしれません(値をグローバルに詰め込み、解放するためにもう使用されていないことがわかっているある時点でそれを取得するなど)が、それはコードに触れなければならない次の男にさらに混乱を引き起こしたでしょう。

個人的に私はそもそもそのようにコードを記述しなかったでしょうが、私たちのほとんどは常に手付かずの適切に設計されたコードベースで作業することができず、時にはこれらのことを実用的に見る必要があります。150バイトのリークがアルゴリズムの改善に使用され、メガバイトのRAMを削り落としたことを修正するのにかかった時間。

最終的に、ラムのギグを使用して専用マシンで実行したアプリで150バイトをリークすることは修正する価値がないと判断したため、リークされたと修正するために何を変更する必要があるかについてコメントを書きましたそれ、そしてなぜそれが当時その価値がなかったのか。


スマート。特に、リークは初期化中に発生したため、アプリケーションの実行時に蓄積されません。
デミ

5

ほとんどの回答は実際のメモリリークに集中していますが(これはだらしのないコーディングの兆候であるため、これは大丈夫ではありません)、質問のこの部分はより興味深いように見えます。

メモリを割り当てて、アプリケーションの最後のコード行(たとえば、グローバルオブジェクトのデコンストラクタ)まで使用するとどうなるでしょうか。メモリ使用量が時間の経過とともに増加しない限り、アプリケーションが終了したときに(Windows、Mac、およびLinuxで)OSがメモリを解放することを信頼しても大丈夫ですか?OSによって解放されるまでメモリが継続的に使用されていた場合、これを実際のメモリリークと見なしますか?

関連付けられているメモリが使用されている場合、プログラムが終了する前にメモリを解放することはできません。解放がプログラム出口によって行われるか、OSによって行われるかは関係ありません。これが文書化されている限り、その変更によって実際のメモリリークが発生することはなく、画像にC ++デストラクタまたはCクリーンアップ関数が含まれていない限り、閉じられていないファイルは、リークされたFILEオブジェクトによって明らかになる可能性がありますが、fclose()がないと、バッファがフラッシュされない可能性があります。

したがって、元のケースに戻ると、それ自体はIMHOで完全に問題ありません。そのため、最も強力なリーク検出器の1つであるValgrindは、要求された場合にのみそのようなリークを処理します。Valgrindでは、ポインタを事前に解放せずに上書きすると、メモリリークと見なされます。これは、再度発生してヒープが際限なく増大する可能性が高いためです。

次に、まだ到達可能なnfreedメモリブロックはありません。出口ですべてを確実に解放することもできますが、それだけでは時間の無駄です。重要なのは、それらが以前に解放される可能性があるかどうかです。メモリ使用量を減らすことは、どのような場合でも役立ちます。


うわー...メモリリークが何であるか知っている人。
Simon Buchan

4

私はvfilbyに同意します-場合によります。Windowsでは、メモリリークを比較的深刻なバグとして扱います。ただし、コンポーネントに大きく依存します。

たとえば、めったに実行されないコンポーネントや限られた期間のメモリリークはそれほど深刻ではありません。これらのコンポーネントは実行され、作業を行ってから終了します。それらがすべて終了すると、メモリは暗黙的に解放されます。

ただし、サービスやその他の長時間実行コンポーネント(シェルなど)のメモリリークは非常に深刻です。その理由は、これらのバグが時間の経過とともにメモリを「盗む」ためです。これを回復する唯一の方法は、コンポーネントを再起動することです。ほとんどの人はサービスまたはシェルを再起動する方法を知りません–したがって、システムのパフォーマンスが低下した場合は、再起動するだけです。

したがって、リークがある場合-2つの方法でその影響を評価します

  1. ソフトウェアとユーザーのエクスペリエンスに。
  2. システムリソースを節約するという観点から、システム(およびユーザー)に。
  3. メンテナンスと信頼性に対する修正の影響。
  4. 他の場所で回帰を引き起こす可能性。

フォアデッカー


3.ソフトウェアのメンテナンスへの影響。
peterchen 2009

3

「既知の」メモリリークが大混乱を引き起こさないと確信している場合でも、それを行わないでください。せいぜい、異なる時間と場所で、似たような、おそらくより重大な間違いをする可能性があります。

私にとってこれを尋ねるのは、「誰もいない朝の午前3時に赤信号を消すことはできますか?」と質問するようなものです。確かにその時は問題ないかもしれませんが、ラッシュアワーで同じことをするための手段を提供します!


3

いいえ、OSがクリーンアップするリークがあってはなりません。理由(私が確認できる限り上記の回答では言及されていません)は、main()が別のプログラムの関数/モジュールとしていつ再利用されるかわからないためです。main()が他の人のソフトウェアで頻繁に呼び出される関数になると、このソフトウェアにはメモリリークが発生し、時間の経過とともにメモリを消費します。

KIV


3

メモリリーク(つまり、システムパフォーマンスに対するメモリリークの影響をテストする)を目的としたプログラムを作成している場合は問題ないと思います。


3

メモリリークが実際に何であるかについて、多くの誤った定義を目にして驚いています。具体的な定義がなければ、それが悪いことかどうかの議論はどこにも行きません。

一部のコメンターが正しく指摘しているように、メモリリークは、プロセスによって割り当てられたメモリが、プロセスがそれを参照または削除できなくなった範囲でスコープから外れた場合にのみ発生します。

ますます多くのメモリを取得しているプロセスは、必ずしもリークしているわけではありません。そのメモリを参照および割り当て解除できる限り、プロセスの明示的な制御下にあり、リークしていません。特にメモリが制限されているシステムのコンテキストでは、プロセスがうまく設計されていない可能性がありますが、これはリークと同じではありません。逆に、たとえば32バイトのバッファのスコープを失うことは、リークされるメモリの量が少ない場合でも、依然としてリークです。これが重要ではないと思われる場合は、誰かがライブラリの呼び出しにアルゴリズムをラップして、それを10,000回呼び出すまで待ち​​ます。

どんなに小さなものでも、自分のコードでリークを許可する理由はまったくありません。CやC ++などの最新のプログラミング言語は、プログラマーがこのようなリークを防止するのに役立つため、特にリークを防止するために、特定の言語機能と組み合わせた場合に、優れたプログラミング手法を採用しないことはめったにありません。

既存またはサードパーティのコードについて、変更の品質または能力の制御が非常に制限されている可能性がある場合、リークの重大度によっては、プロセスを定期的に再起動するなどの軽減アクションを受け入れるか、軽減するアクションを強制される場合があります。リークの影響。

既存の(漏洩)コードを変更または置換できない可能性があるため、それを受け入れる必要がある場合があります。ただし、これは問題ないと宣言することとは異なります。


2

それが意図的なものである場合はリークではなく、大量のメモリでない限り問題ではないか、大量のメモリになる可能性があります。プログラムの存続期間中にグローバル割り当てをクリーンアップしないことはかなり一般的です。リークがサーバーまたは実行時間の長いアプリにある場合、時間の経過とともに増大し、それが問題となります。


2

あなたはあなた自身の質問に答えたと思います。最大の欠点は、それらがメモリリーク検出ツールにどのように干渉するかですが、特定の種類のアプリケーションでは、欠点が大きな欠点であると思います。

私は、堅牢であるはずのレガシーサーバーアプリケーションを使用していますが、リークがあり、グローバルがメモリ検出ツールの邪魔をしています。それは大きな問題です。

Jared Diamond著の「Collapse」の本で、著者はイースター島の最後の木、つまり島から降りるためにカヌーを造るために必要だった木を伐採した男が何を考えていたのか不思議に思っています。その最初のグローバルがコードベースに追加されたのは何年も前の日だと思います。それは捕らえられるべきだった日だった。


2

このようなすべてのシナリオの質問と同じ問題が発生します。プログラムが変更され、突然メモリリークが1000万回呼び出され、プログラムの最後が別の場所にあるため、問題が発生するとどうなりますか?ライブラリにある場合は、ライブラリのメンテナにバグを記録してください。自分のコードにリークを入れないでください。


その場合、メモリリークの影響が変化するため、リークの差し込みの優先度を再評価する必要があります。
John Dibling 2008年

@ジョン:少なくともリークを文書化する方がいいです。それでも、赤く点滅する大きなコメントを無視したり、とにかくリークの多いコードをコピーアンドペーストしたりしないでください。そもそも誰かにそうする能力を与えたくないのです。
tloach 2008年

2

私は答えません。

理論的には、混乱が残った場合、オペレーティングシステムは後をクリーンアップします(これは失礼ですが、コンピュータには感情がないため、許容できる場合があります)。ただし、プログラムの実行時に発生する可能性のあるすべての状況を予測することはできません。したがって、(何らかの動作の正式な証明を行うことができる場合を除き)、メモリリークを作成することは専門家の観点からは無責任でずさんです。

サードパーティのコンポーネントがメモリリークを起こした場合、差し迫った影響だけでなく、プログラマーがずさんに動作し、他のメトリックスにも影響を与える可能性があることを示しているため、これを使用することに対する非常に強い議論です。さて、レガシーシステムを検討する場合、これは困難です(Webブラウジングコンポーネントを検討してください。私の知る限り、すべてメモリリークします)。


2

歴史的に、それはいくつかのエッジケースの下のいくつかのオペレーティングシステムで問題でした。これらのエッジケースは将来存在する可能性があります。

次に例を示します。Sun3時代のSunOSで、プロセスがexec(またはより伝統的にはforkしてからexec)を使用すると、後続の新しいプロセスが親と同じメモリフットプリントを継承し、縮小できないという問題がありました。 。親プロセスがメモリの1/2ギガを割り当て、execを呼び出す前にそれを解放しなかった場合、子プロセスは同じ1/2ギガを使用し始めます(割り当てられていなくても)。この動作は、メモリを独占していたSunTools(デフォルトのウィンドウシステム)が最もよく示しました。生成されたすべてのアプリは、fork / execを介して作成され、SunToolsのフットプリントを継承し、スワップスペースをすぐに使い果たしました。


2

これはすでに吐き気について議論されました。つまり、メモリリークはバグであり、修正する必要があります。サードパーティのライブラリがメモリをリークすると、それが他に何が問題であるのか疑問に思いますか?車を製造している場合、オイルを時々漏らすエンジンを使用しますか?結局のところ、誰かがエンジンを作ったので、それはあなたのせいではなく、あなたはそれを修正することはできませんよね?


しかし、時々オイルが漏れるエンジンを搭載した車を所有している場合、それを修理するためにお金を費やすか、またはオイルのレベルに注意を払い、時々それを補充しますか。答えはあらゆる種類の要因に依存します。
スリム

これは車の所有についてではありません。これは車を作ることについてです。メモリリークのあるサードパーティのライブラリを入手し、それを絶対に使用する必要がある場合は、それを使用します。ただし、システムまたはライブラリを作成している場合は、バグがないことを確認する必要があります。
ディマ

+1は他のバグと同様に扱います。(それは私の本の中で「即座に修正する」という意味ではありませんが、確かに「修正する必要がある」)
peterchen

2

一般に、スタンドアロンアプリケーションのメモリリークは、プログラムの終了時にクリーンアップされるため、致命的ではありません。

終了しないように設計されたサーバープログラムに対して何をしますか?

リソースが正しく割り当てられ、解放されるコードを設計および実装しないプログラマーの場合、私はあなたやあなたのコードとは何の関係もありません。リークしたメモリをクリーンアップする必要がない場合は、ロックはどうですか?それらもそこにぶら下がっていますか?さまざまなディレクトリに配置されている一時ファイルの小さな固まりを残していますか?

そのメモリをリークし、プログラムにクリーンアップさせますか?いいえ、絶対にありません。バグ、バグ、その他のバグにつながるのは悪い習慣です。

自分の後を片付ける。ヨママはここではもう働かない。


私はスレッドではなくプロセスを故意に使用するサーバープログラムに取り組んだので、メモリリークとセグメンテーションエラーが原因で発生する損傷は限られています。
スリム

興味深いアプローチ。終了に失敗し、メモリを大量に消費し続けるプロセスについて少し心配です。
EvilTeach 2008

2

原則として、回避できないと感じるメモリリークが発生した場合は、オブジェクトの所有権についてより深く考える必要があります。

しかし、あなたの質問に対する一言で言えば、私の答えは「量産コードでは、はい」です。開発中はありません。これは逆に見えるかもしれませんが、ここに私の推論があります:

あなたが説明する状況では、プログラムの最後までメモリが保持されているので、メモリを解放しないことは完全に問題ありません。プロセスが終了すると、OSはとにかくクリーンアップします。実際、これはユーザーエクスペリエンスを向上させる可能性があります。私が取り組んだゲームでは、プログラマーは終了する前にすべてのメモリを解放する方がきれいだと考え、プログラムのシャットダウンに最大30分かかりました。代わりに、exit()を呼び出しただけの簡単な変更により、プロセスはすぐに消え、ユーザーは本来あるべきデスクトップに戻りました。

ただし、デバッグツールについてはあなたの言うとおりです。それらは適合をスローし、すべての誤検知により、実際のメモリリークの発見が困難になる可能性があります。そのため、メモリを解放するデバッグコードを常に記述し、出荷時に無効にしてください。

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