607

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

まず、次のようなコードがあるとします。

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

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

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

4

18 に答える 18

59

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

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

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

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

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

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

于 2009-12-28T06:04:22.633 に答える
25

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

だから、もしそうなら、私は今漏れを持っています。

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

  1. ユーザーが「unset a」と入力しました
  2. 信号を処理するか、ユーザーが「quit」と入力して、クリーンアップ関数に入りました

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

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

char *t;

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

それがフォーク/デーモン化 (理論的には永久に実行される) の前に発生した場合、プログラムは t 255 回の不確定なサイズをリークしただけです。

適切に作成された優れたプログラムは、常にそれ自体をクリーンアップする必要があります。すべてのメモリを解放し、すべてのファイルをフラッシュし、すべての記述子を閉じ、すべての一時ファイルのリンクを解除するなど。クラッシュを検出して再開します。

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

于 2009-03-18T08:36:05.567 に答える
11

このコードは通常問題なく動作しますが、コードの再利用の問題を考慮してください。

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

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

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

于 2009-03-18T08:00:51.563 に答える
6

実際、オペレーティング システムの学部課程のOSTEPオンライン 教科書には、まさにあなたの質問について説明しているセクションがあります。

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

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

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

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

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


編集:抜粋で言及されている余談を以下にコピーします。

余談:プロセスが終了してもメモリがリークしない理由

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

したがって、短期間のプログラムの場合、メモリ リークが原因で動作上の問題が発生することはほとんどありません (ただし、不適切な形式と見なされる場合もあります)。実行時間の長いサーバー (終了しない Web サーバーやデータベース管理システムなど) を作成する場合、メモリ リークははるかに大きな問題であり、アプリケーションがメモリ不足になると、最終的にはクラッシュにつながります。そしてもちろん、特定のプログラム、つまりオペレーティング システム自体の内部では、メモリ リークはさらに大きな問題となります。もう一度お見せしましょう: カーネルコードを書く人は、誰よりも大変な仕事をしています...

のメモリ APIの章のページ 7 から

Operating Systems: Three Easy Pieces
Remzi H. Arpaci-Dusseau および Andrea C. Arpaci-Dusseau Arpaci-Dusseau Books 2015 年 3 月 (バージョン 0.90)

于 2017-10-17T01:37:49.843 に答える