web-dev-qa-db-ja.com

mallocの後に解放しないと本当に何が起こりますか?

これは何年も私を悩ませてきました。

私たちは皆、学校で教えられています(少なくとも、私はそうでした)割り当てられたすべてのポインターを解放しなければなりません。ただし、メモリを解放しない場合の実際のコストについては少し興味があります。 mallocがループ内またはスレッド実行の一部内で呼び出される場合など、いくつかの明らかなケースでは、メモリリークがないように解放することが非常に重要です。ただし、次の2つの例を考慮してください。

まず、次のようなコードがある場合:

int main()
{
    char *a = malloc(1024);
    /* Do some arbitrary stuff with 'a' (no alloc functions) */
    return 0;
}

ここでの本当の結果は何ですか?私の考えでは、プロセスは終了し、ヒープスペースはとにかくなくなってしまうので、freeへの呼び出しを見逃しても害はありません(ただし、閉鎖、保守性、および優れた実践のためにそれを保持することの重要性は認識しています)。私はこの考えに正しいですか?

第二に、シェルのように動作するプログラムがあるとします。ユーザーはaaa = 123のような変数を宣言でき、それらは後で使用するために何らかの動的データ構造に格納されます。明らかに、* alloc関数(ハッシュマップ、リンクリスト、そのようなもの)を呼び出すソリューションを使用することは明らかです。この種のプログラムでは、mallocを呼び出した後に解放することは意味がありません。これらの変数はプログラムの実行中に常に存在している必要があり、静的に割り当てられたスペースでこれを実装する良い方法がないからです(わかります)。割り当てられているが、プロセス終了の一部としてのみ解放されるメモリの束を持つのは悪い設計ですか?もしそうなら、代替手段は何ですか?

486
Scott

ほぼすべての最新のオペレーティングシステムは、プログラムの終了後に割り当てられたすべてのメモリスペースを回復します。私が考えることができる唯一の例外は、プログラムの静的ストレージとランタイムメモリがほぼ同じであるPalm OSのようなものである可能性があるため、解放しないとプログラムがより多くのストレージを占有する可能性があります。 (私はここで推測しているだけです。)

したがって、一般に、必要以上のストレージを保持するという実行時コストを除いて、害はありません。確かに、あなたが与える例では、クリアされるまで使用される可能性のある変数のメモリを保持したいと思います。

ただし、不要になったらすぐにメモリを解放し、プログラムの終了時にまだ残っているものをすべて解放するのが良いスタイルと考えられています。これは、使用しているメモリを把握し、それがまだ必要かどうかを考えるための演習です。追跡しないと、メモリリークが発生する可能性があります。

一方、終了時にファイルを閉じるという同様の警告は、より具体的な結果をもたらします-そうしないと、それらに書き込んだデータがフラッシュされないか、一時ファイルである場合は、フラッシュされない可能性があります完了したら削除されます。また、データベースハンドルは、トランザクションをコミットし、完了したら閉じます。同様に、C++やObjective Cのようなオブジェクト指向言語を使用している場合、処理が完了してもオブジェクトを解放しないと、デストラクタが呼び出されることはなく、クラスが担当するリソースはクリーンアップされない可能性があります。

341
Paul Tomblin

はい、あなたは正しいです、あなたの例は害をしません(少なくともほとんどの最新のオペレーティングシステムでは)。プロセスによって割り当てられたすべてのメモリは、プロセスが終了するとオペレーティングシステムによって回復されます。

ソース: Allocation and GC Myths (PostScriptアラート!)

割り当て神話4:ガベージコレクションされていないプログラムは、割り当てたすべてのメモリの割り当てを常に解除する必要があります。

真実:頻繁に実行されるコードで割り当てを省略すると、リークが増大します。それらはめったに受け入れられません。ただし、プログラムの終了まで割り当てられたメモリのほとんどを保持するプログラムは、多くの場合、割り当て解除を介在させることなくパフォーマンスが向上します。空きがない場合、mallocの実装ははるかに簡単です。

ほとんどの場合、プログラムの終了直前にメモリの割り当てを解除しても意味がありません。OSはとにかくそれを再利用します。無料は、死んだオブジェクトに触れてページングします。 OSはしません。

結果:割り当てをカウントする「リークディテクタ」に注意してください。いくつかの「リーク」は良いです!

とは言っても、すべてのメモリリークを回避するように努めるべきです。

2番目の質問:デザインは大丈夫です。アプリケーションが終了するまで何かを保存する必要がある場合は、動的メモリ割り当てでこれを行うことができます。必要なサイズが事前にわからない場合は、静的に割り当てられたメモリを使用できません。

105
compie

===将来の校正コードの再利用はどうですか? ===

オブジェクトを解放するためのコードをdo n'tしない場合、メモリに依存できる場合にのみ安全に使用できるようにコードを制限していますプロセスが閉じられることによって解放されます。つまり、小さな1回限りの使用プロジェクトまたは「破棄」[1] プロジェクト)...プロセスがいつ終了するかがわかります。

動的に割り当てられたすべてのメモリをfree()するコードをdo書く場合、将来的にコードを校正し、他の人がそれをより大きなもので使用できるようにするプロジェクト。


[1]「使い捨て」プロジェクトに関して。 「使い捨て」プロジェクトで使用されるコードには、捨てられない方法があります。次に10年が経過し、「使い捨て」コードがまだ使用されていることを知っています。

ハードウェアの動作を改善するために、楽しみのためだけにコードを書いた人の話を聞きました。彼は「 ただの趣味で、大きくて専門的ではない 」と言った。数年後、多くの人々が彼の「趣味」コードを使用しています。

52

あなたは正しいです、害はありませんし、ただ終了する方が速いです

これにはさまざまな理由があります。

  • すべてのデスクトップおよびサーバー環境は、exit()でメモリ空間全体を解放するだけです。それらは、ヒープなどのプログラム内部のデータ構造を認識しません。

  • ほとんどすべてのfree()実装は、とにかくメモリをオペレーティングシステムに返しません

  • さらに重要なことは、exit()の直前に行うのは時間の無駄です。終了時に、メモリページとスワップ領域が単に解放されます。対照的に、一連のfree()呼び出しはCPU時間を消費し、ディスクページング操作、キャッシュミス、およびキャッシュエビクションを引き起こす可能性があります。

無意味な操作の確実性を正当化する将来のコード再利用の可能性について:これは考慮事項ですが、間違いなく Agile の方法ではありません。 YAGNI!

47
DigitalRoss

通常、割り当てが完了したら、割り当てられたすべてのブロックを解放します。今日、私のプログラムのエントリポイントはmain(int argc, char *argv[])かもしれませんが、明日はfoo_entry_point(char **args, struct foo *f)であり、関数ポインタとして入力されます。

それで、もしそれが起こったら、私は今漏れがあります。

2番目の質問に関して、私のプログラムがa = 5のような入力を受け取った場合、aにスペースを割り当てるか、後続のa = "foo"に同じスペースを再割り当てします。これは、次の状態になるまで割り当てられたままになります。

  1. ユーザーが「unset a」と入力した
  2. シグナルを処理するか、ユーザーが「quit」と入力して、クリーンアップ機能を開始しました

プロセスの終了後にメモリを再利用しないmodern OSは考えられません。もう一度、free()は安いので、クリーンアップしてみませんか?他の人が言ったように、valgrindのようなツールは、あなたが本当に心配する必要があるリークを見つけるのに最適です。あなたの例のブロックは 'still reachable'としてラベル付けされますが、漏れがないことを保証しようとするとき、出力の余分なノイズだけです。

別の神話は「main()にある場合、解放する必要はありません」です。これは正しくありません。以下を考慮してください。

char *t;

for (i=0; i < 255; i++) {
    t = strdup(foo->name);
    let_strtok_eat_away_at(t);
}

それがfork/daemonizing(そして理論的には永久に実行)の前に来たなら、あなたのプログラムはt 255回の未定サイズをリークしたばかりです。

優れた、よく書かれたプログラムは、常にそれ以降をクリーンアップします。すべてのメモリを解放し、すべてのファイルをフラッシュし、すべての記述子を閉じ、すべての一時ファイルのリンクを解除します。クラッシュを検出して再開します。

本当に、あなたが他のものに移るときあなたのものを維持しなければならない貧しい魂に親切にしてください..彼らに「valgrind clean」を渡してください:)

22
Tim Post

私は、OPが正しい、または害がないと言うすべての人に完全に反対します。

誰もが現代および/またはレガシーOSについて話している。

しかし、OSがまったくない環境にいる場合はどうなりますか?何もないところ?

今、スレッドスタイルの割り込みを使用し、メモリを割り当てていると想像してください。 C標準では、ISO/IEC:9899はメモリの寿命です。

7.20.3メモリ管理機能

1 calloc、malloc、およびrealloc関数の連続呼び出しによって割り当てられるストレージの順序と連続性は指定されていません。割り当てが成功した場合に返されるポインターは、任意のタイプのオブジェクトへのポインターに割り当てられ、割り当てられたスペース内のそのようなオブジェクトまたはそのようなオブジェクトの配列にアクセスするために適切に位置合わせされます(スペースが明示的に割り当て解除されるまで) 。割り当てられたオブジェクトの有効期間は、割り当てから割り当て解除まで延長されます。[...]

そのため、環境が解放作業を行っていることを示す必要はありません。それ以外の場合は、最後の文に「またはプログラムが終了するまで」追加されます。

つまり、メモリを解放しないことは単に悪い習慣ではありません。 C準拠コードではなく、非移植性を生成します。これは、少なくとも「次の場合に正しいと見なすことができます:[...]、環境でサポートされている」.

しかし、OSがまったくない場合、誰もあなたのために仕事をしていません(一般に、組み込みシステムでメモリの割り当てと再割り当てを行わないことを知っていますが、必要な場合もあります)。

したがって、一般的なプレーンC(OPがタグ付けされている)で言えば、これは単にエラーのある移植性のないコードを生成しているだけです。

21
dhein

終了時にメモリを解放しないでおくのはまったく問題ありません。 malloc()は、「ヒープ」と呼ばれるメモリ領域からメモリを割り当て、プロセスが終了すると、プロセスの完全なヒープが解放されます。

そうは言っても、人々が終了前にすべてを解放するのが良いと主張する理由の1つは、メモリデバッガ(Linuxのvalgrindなど)が解放されていないブロックをメモリリークとして検出し、「実際の」メモリリークがある場合は最後に「偽の」結果も得られる場合、それらを見つけるのはより困難です。

12
Antti Huima

通常、このコードは正常に機能しますが、コードの再利用の問題を考慮してください。

割り当てられたメモリを解放しないコードスニペットを作成した可能性があります。メモリが自動的に再利用されるように実行されます。大丈夫そうです。

その後、他の誰かがあなたのスニペットを1秒間に1,000回実行されるように自分のプロジェクトにコピーします。その人は今、彼のプログラムで巨大なメモリリークを抱えています。一般的にはあまり良くありませんが、通常はサーバーアプリケーションにとって致命的です。

企業ではコードの再利用が一般的です。通常、会社は従業員が作成するすべてのコードを所有し、すべての部門は会社が所有するものを再利用できます。そのため、このような「無邪気に見える」コードを書くことで、他の人に頭痛の種となる可能性があります。これにより解雇される場合があります。

11
sharptooth

割り当てたメモリを使用している場合は、何も問題はありません。メモリを解放せずに、プログラムの残りの部分で使用可能にせずに割り当てる関数(メイン以外)を作成すると、問題になります。その後、プログラムは割り当てられたメモリで実行を続けますが、使用する方法はありません。あなたのプログラムおよび他の実行中のプログラムはそのメモリを奪われています。

編集:他の実行中のプログラムがそのメモリを奪われていると言うのは100%正確ではありません。オペレーティングシステムは、プログラムを仮想メモリ(</handwaving>)にスワップアウトすることを犠牲にして、いつでも使用できるようにすることができます。しかし、ポイントは、プログラムが使用していないメモリを解放する場合、仮想メモリスワップが必要になる可能性は低いということです。

11
Bill the Lizard

ここでの本当の結果は何ですか?

プログラムがメモリをリークしました。OSによっては、mayが回復された可能性があります。

最新のデスクトップオペレーティングシステムdoプロセス終了時にリークしたメモリを回復します。ここで他の多くの回答で見られます)

ただし、信頼すべきではない安全機能に依存しているため、プログラム(または機能)が、この動作doesが発生するシステムで実行される可能性があります「ハード」メモリリーク、next time。

カーネルモード、またはトレードオフとしてメモリ保護を採用していないビンテージ/組み込みオペレーティングシステムで実行している可能性があります。 (MMUはダイスペースを占有し、メモリ保護には追加のCPUサイクルがかかります。プログラマーから後片付けを依頼するのはそれほど多くありません)。

任意の方法でメモリを使用および再利用できますが、終了する前にすべてのリソースの割り当てを解除してください。

5
DevSolar

OSTEP オンラインテキストブックには、オペレーティングシステムの学部課程のセクションがあり、あなたの質問を正確に説明しています。

関連するセクションは、6ページの メモリAPIの章 の「メモリの解放を忘れる」で、次の説明があります。

場合によっては、free()を呼び出さないことが合理的であるように思われるかもしれません。たとえば、プログラムは短命であり、すぐに終了します。 この場合、プロセスが終了すると、OSは割り当てられたページをすべてクリーンアップするため、メモリリークは発生しません。これは確かに「機能」しますが(7ページの余談を参照)、おそらく開発するのは悪い習慣なので、そのような戦略を選択する場合は注意してください

この抜粋は、仮想メモリの概念を導入することを目的としています。基本的に本のこの時点で、著者はオペレーティングシステムの目標の1つが「メモリの仮想化」、つまり、すべてのプログラムが非常に大きなメモリアドレス空間にアクセスできると信じることであると説明しています。

舞台裏では、オペレーティングシステムは、ユーザーに表示される「仮想アドレス」を物理メモリを指す実際のアドレスに変換します。

ただし、物理メモリなどのリソースを共有するには、オペレーティングシステムが使用しているプロセスを追跡する必要があります。したがって、プロセスが終了した場合、プロセスを再配布して他のプロセスと共有できるように、プロセスのメモリを回収することはオペレーティングシステムの機能と設計目標の範囲内です。


EDIT:抜粋で言及した脇を以下にコピーします。

サイド:メモリが存在しない理由ISプロセスが終了するとリークされる

短命のプログラムを作成するときは、malloc()を使用してスペースを割り当てることができます。プログラムが実行され、完了しようとしています。終了する直前にfree()を何度も呼び出す必要がありますか?間違っていないように見えますが、実際の意味で「失われる」メモリはありません。理由は簡単です。システムには実際に2つのレベルのメモリ管理があります。メモリ管理の第1レベルは、OSによって実行されます。OSは、実行時にプロセスにメモリを渡し、プロセスが終了する(または終了する)ときにメモリを取り戻します。第2レベルの管理は、各プロセス内、たとえばmalloc()およびfree()を呼び出すときのヒープ内にあります。 free()の呼び出しに失敗した場合(したがって、ヒープ内のメモリをリークする場合)、プログラムが終了すると、オペレーティングシステムはプロセスのすべてのメモリ(コード、スタック、および関連するヒープを含む)を回収しますランニング。アドレス空間のヒープの状態に関係なく、OSはプロセスが終了するとそれらのページをすべて取り戻します。したがって、解放しなかったにもかかわらずメモリが失われないようにします。

したがって、短命のプログラムの場合、メモリリークは多くの場合、操作上の問題を引き起こしません(ただし、貧弱なフォームと見なされる場合があります)。長時間実行されるサーバー(Webサーバーやデータベース管理システムなど、決して終了しない)を作成する場合、リークされたメモリは非常に大きな問題であり、アプリケーションがメモリ不足になると最終的にクラッシュにつながります。そしてもちろん、メモリリークは、特定のプログラム(オペレーティングシステム自体)内でさらに大きな問題です。もう一度私たちを見せてください:カーネルコードを書く人は、最も難しい仕事をしています...

の7ページ目 メモリAPI の章

オペレーティングシステム:3つの簡単なピース
Remzi H. Arpaci-DusseauおよびAndrea C. Arpaci-Dusseau Arpaci-Dusseau Books 2015年3月(バージョン0.90)

4
spenceryue

実際の危険変数を解放しないことはありませんが、最初のブロックを解放せずにメモリブロックへのポインタを別のメモリブロックに割り当てると、最初のブロックはアクセスできなくなりますが、まだスペースアップ。これはメモリリークと呼ばれるものです。定期的にこれを行うと、プロセスはより多くのメモリを消費し始め、他のプロセスからシステムリソースを奪います。

プロセスが短命である場合、プロセスが完了すると、割り当てられたすべてのメモリがオペレーティングシステムによって回収されるため、これを行うことでしばしば逃げることができますが、それ以上使用しないメモリをすべて解放する習慣をつけることをお勧めします。

3
Kyle Cronin

あなたはその点で絶対に正しいです。プログラムが終了するまで変数が存在しなければならない小さな些細なプログラムでは、メモリの割り当てを解除しても本当の利点はありません。

実際、私はかつて、プログラムの各実行が非常に複雑であるが比較的短命であるプロジェクトに関与していました。決定は、メモリの割り当てを維持し、ミスを解除してプロジェクトを不安定にしないことでした。

そうは言っても、ほとんどのプログラムではこれは実際にはオプションではないか、メモリ不足になる可能性があります。

3
Uri

プロセスが終了すると、メモリは自動的に解放されます。一部の人々は、プロセスが終了したときに大規模なクリーンアップを行わないよう努めています。これは、すべてのプロセスがオペレーティングシステムに放棄されるためです。ただし、プログラムの実行中は、未使用のメモリを解放する必要があります。そうしないと、ワーキングセットが大きくなりすぎると、最終的には不足するか、過剰なページングを引き起こす可能性があります。

2
Michael

アプリケーションをゼロから開発している場合は、いつ電話をかけるかについて知識のある選択をすることができます。サンプルプログラムは問題ありません。メモリを割り当て、数秒間動作させてから閉じて、要求したすべてのリソースを解放します。

ただし、サーバー/長時間実行アプリケーション、または他の誰かが使用するライブラリなど、他の何かを書いている場合は、mallocですべてを無料で呼び出すことを期待する必要があります。

実用的な側面を少し無視すると、より厳密なアプローチを採用し、mallocをすべて解放するように強制する方がはるかに安全です。コードを作成するたびにメモリリークを監視する習慣がない場合は、簡単にいくつかのリークが発生する可能性があります。言い換えれば、はい-それなしで逃げることができます。ただし、注意してください。

2
ojrac

プログラムが終了する前に数メガバイトを解放するのを忘れると、オペレーティングシステムはそれらを解放します。しかし、プログラムが一度に数週間実行され、プログラム内のループが各反復で数バイトを解放するのを忘れると、通常の方法で再起動しない限り、コンピューターの利用可能なすべてのメモリを食い尽くす強力なメモリリークが発生します基本=>プログラムが元々は非常に大きなタスクに使用されていたとしても、それが元々そのために設計されていなかったとしても、小さなメモリリークでさえ悪いかもしれません。

0