割り当てられたメモリでfree()を使用しないことは問題ありませんか?


83

私はコンピュータ工学を勉強していて、いくつかの電子工学コースを持っています。私はそれを使用して回避することができる(これらのコースの)私の教授の2から、聞いたfree()(後に機能をmalloc()calloc()おそらく割り当てられたメモリ空間は、他のメモリを割り当てるために再び使用されることはありませんので、など)。つまり、たとえば、4バイトを割り当ててから解放すると、4バイトのスペースができて、再び割り当てられない可能性があります。つまり、穴ができます。

私はそれがおかしいと思います:あなたはそれを解放せずにヒープにメモリを割り当てるおもちゃでないプログラムを持つことはできません。しかし、私には、それぞれにmalloc()がなければならないほど重要である理由を正確に説明する知識がありませんfree()

だから:使用せmalloc()ずに使用することが適切であるかもしれない状況はありfree()ますか?そうでない場合は、どうすればこれを教授に説明できますか?


11
それらは「間違っている」わけではありません-非常に小さな孤立した自由領域の断片化について有効な(限定されている場合)ポイントがあり、おそらくあなたが報告したよりも少し注意深く述べています。
クリス・ストラットン2014年

2
メモリを解放する必要がないのは、管理対象メモリを使用する場合、または元の割り当てられたメモリを再利用する場合のみです。2人のインストラクターがこれを言ったのは、あなたがメモリを再利用できる特定のプログラムについて話しているからだと思います。この場合でも、free()を使用しますが、アプリケーションの最後でのみ使用します。これが彼らの意味ではなかったと確信していますか?
krowe 2014年

17
@Marian:CおよびC ++では、割り当てられたのと同じ.c /.cxxファイルで定義された関数で割り当てられたメモリを解放する必要があると教授が主張しました...これらの人々は時々重度の低酸素症に苦しんでいるようですアイボリータワーで高すぎる生活をしているため。
PlasmaHH 2014年

4
メモリの割り当てを解除しないおもちゃ以外のプログラムはかなりあります。プロセスの終了時にOSにすべてのメモリをクリーンアップさせる方が、(大騒ぎして)たくさんの簿記を保持するよりもはるかに高速なので、自分で行うことができます。
ドナーフェロー

9
聞いたことを疑う余地なく脳にマージしないでください。私には、間違っていたり時代遅れだったりした多くの教師や講師、訂正者がいます。そして、常に彼らの言うことを非常に正確に分析してください。私たちの人々はしばしば非常に正確で、正しいことを言うかもしれませんが、一般的な用語でしか家にいないと感じる人によって、間違って、または間違った優先順位で理解しやすいです。たとえば、学校で先生が「宿題をしましたか」と言ったのを覚えています。「いいえ」と言いました。私は正しかったのですが、先生はこれを不快に感じました。なぜなら、彼が予期していなかった下手な言い訳を見つける時間を節約できたからです。
セバスチャンマッハ

回答:


100

簡単:ほとんどすべての半真面目なmalloc()/free()実装のソースを読むだけです。これは、呼び出しの作業を処理する実際のメモリマネージャーを意味します。これは、ランタイムライブラリ、仮想マシン、またはオペレーティングシステムにある可能性があります。もちろん、コードはすべての場合に等しくアクセスできるわけではありません。

隣接する穴をより大きな穴に結合することにより、メモリが断片化されていないことを確認することは非常に一般的です。より深刻なアロケーターは、これを確実にするためにより深刻な技術を使用します。

したがって、3つの割り当てと割り当て解除を実行し、ブロックを次の順序でメモリに配置するとします。

+-+-+-+
|A|B|C|
+-+-+-+

個々の割り当てのサイズは重要ではありません。次に、最初と最後のAとCを解放します。

+-+-+-+
| |B| |
+-+-+-+

最終的にBを解放すると、(最初は少なくとも理論的には)次のようになります。

+-+-+-+
| | | |
+-+-+-+

断片化を解除して

+-+-+-+
|     |
+-+-+-+

つまり、1つの大きな空きブロックで、フラグメントは残っていません。

要求に応じて、参照:


1
参考資料を教えていただけますか?
ニック

4
仮想アドレス空間は物理メモリを直接表現したものではないことは言及する価値があると思います。また、物理メモリの断片化はOSで処理できますが、プロセスによって解放されない仮想メモリも物理的に解放されません。
lapk 2014年

@PetrBudnikとにかく、仮想メモリが1対1で物理メモリにマップされることはまれです。OSはページマッピングを考慮し、最小限の手間でスワップインおよびスワップアウトできるようになります
ラチェットフリーク

3
非常につまらないコメント:概念は十分に説明されていますが、実際の例は少し選択されました..不運です。たとえばdlmallocのソースコードを見て混乱している人のために:特定のサイズ未満のブロックは常に2の累乗であり、それに応じてマージ/分割されます。したがって、(おそらく)1つの8バイトブロックと1つの4バイトブロックになりますが、12バイトブロックはありません。これは、少なくともデスクトップ上のアロケータにとってはかなり標準的なアプローチですが、組み込みアプリケーションはオーバーヘッドにもっと注意を払うように努めているかもしれません。
Voo 2014年

@Voo例のブロックのサイズについての言及を削除しましたが、とにかく問題ではありませんでした。良いですか?
2014年

42

他の回答には、すでにその完璧に本物の実装を説明malloc()し、free()より大きな自由チャンクに実際に合体(defragmnent)の穴を行います。しかし、そうではなかったとしても、それを放棄するのは悪い考えです。free()です。

重要なのは、プログラムがこれらの4バイトのメモリを割り当てた(そして解放したい)ということです。長期間実行する場合は、4バイトのメモリを再度割り当てる必要がある可能性があります。したがって、これらの4バイトがより大きな連続したスペースに合体することは決してない場合でも、プログラム自体で再利用できます。


6
+1その通り。問題は、freeパフォーマンスに影響を与えるのに十分な回数呼び出されている場合、それを省略すると使用可能なメモリに非常に大きなへこみができるように、おそらく十分な回数呼び出されているということです。パフォーマンスが継続による苦しみ組込みシステムの状況を想像するのは難しいですfreeが、どこmallocにのみ有限回数と呼ばれているが、データのワンショット処理を実行してからリセットする組み込みデバイスを使用することは、かなりまれな使用例です。
ジェイソンC


9

あなたの教授は、万が一、POSIXを使用していますか?彼らがたくさんの小さくてミニマルなシェルアプリケーションを書くことに慣れているなら、それはこのアプローチがそれほど悪くないだろうと私が想像できるシナリオです-OSの余暇で一度にヒープ全体を解放することAを解放するよりも速くは千の変数。アプリケーションが1〜2秒間実行されると予想される場合は、割り当て解除をまったく行わずに簡単に回避できます。

もちろん、それでも悪い習慣です(パフォーマンスの向上は、漠然とした直感ではなく、常にプロファイリングに基づく必要があります)。他の制約を説明せずに学生に言うべきことではありませんが、小さな配管シェルがたくさん想像できます。 -アプリケーションはこのように記述されます(静的割り当てを完全に使用しない場合)。変数を解放しないことでメリットが得られるものに取り組んでいる場合は、非常に低レイテンシの条件で作業している(この場合、動的割り当てとC ++を購入する余裕はありますか?:D)。非常に、非常に間違ったことをしている(単一のメモリブロックではなく、1000個の整数を次々に割り当てることによって整数配列を割り当てるなど)。


最後にOSにすべてを解放させることは、パフォーマンスだけでなく、動作させるために必要なロジックもはるかに少なくなります。
hugomg 2014年

@missingno言い換えれば、メモリ管理がいかに難しいかを回避することができます:)ただし、管理されていない言語に対する議論として、パフォーマンスではなくロジックを解放することが複雑な理由である場合は、より良いかもしれません。あなたのためにそれを世話する言語/環境を使用することをやめなさい。
ルアン2014年

5

あなたは彼らが電子工学の教授であると言いました。それらはファームウェア/リアルタイムソフトウェアの作成に使用される可能性があり、実行が必要になることが多いものを正確に計時することができました。そのような場合、すべての割り当てに十分なメモリがあることを知っていて、メモリを解放および再割り当てしないと、実行時間の計算がより簡単になる可能性があります。

一部のスキームでは、ハードウェアメモリ保護を使用して、割り当てられたメモリでルーチンが完了したことを確認したり、非常に例外的な場合トラップを生成したりすることもできます。


10
それは良い点です。ただし、その場合はまったく使用mallocせず、代わりに静的割り当てに依存する(または、大きなチャンクを割り当ててから手動でメモリを処理する)ことを期待します。
ルアン2014年

2

これを以前のコメントや回答とは異なる角度から見ると、1つの可能性は、教授がメモリが静的に割り当てられたシステム(つまり、プログラムがコンパイルされたとき)の経験があることです。

静的割り当ては、次のような場合に発生します。

define MAX_SIZE 32
int array[MAX_SIZE];

多くのリアルタイムおよび組み込みシステム(EEまたはCEが遭遇する可能性が最も高いシステム)では、通常、動的メモリ割り当てを完全に回避することが望ましいです。だから、の使用mallocnewおよびその削除の対応はまれです。その上、近年、コンピュータのメモリが爆発的に増加しています。

512 MBが利用可能で、静的に1 MBを割り当てている場合、ソフトウェアが爆発する前に、約511 MBを処理する必要があります(正確には...しかし、ここで私と一緒に行ってください)。511 MBを悪用すると仮定すると、それらを解放せずに毎秒4バイトをmallocすると、メモリが不足する前に73時間近く実行できます。多くのマシンが1日に1回シャットダウンすることを考えると、プログラムのメモリが不足することはありません。

上記の例では、リークは4バイト/秒、つまり240バイト/分です。ここで、そのバイト/分の比率を下げると想像してください。その比率が低いほど、プログラムを問題なく実行できる時間が長くなります。もしあなたのmallocのがまれである、それは本当の可能性です。

ええと、あなたがmalloc一度だけ何かに行くことを知っていて、それmallocが二度とヒットしないことを知っているなら、それは静的割り当てによく似ていますが、あなたが割り当てているもののサイズを知る必要はありません-前面。例:再び512MBあるとしましょう。malloc整数の32配列が必要です。これらは典型的な整数です-それぞれ4バイト。これらの配列のサイズが1024整数を超えることはありません。私たちのプログラムでは、他のメモリ割り当ては発生しません。十分なメモリがありますか?32 * 1024 * 4 = 131,072。128KB-そうです。十分なスペースがあります。これ以上メモリを割り当てないことがわかっている場合は、安全にmallocそれらを解放せずにそれらの配列。ただし、これは、プログラムがクラッシュした場合にマシン/デバイスを再起動する必要があることを意味する場合もあります。プログラムを4,096回開始/停止すると、512MBすべてが割り当てられます。ゾンビプロセスがある場合、クラッシュした後でもメモリが解放されない可能性があります。

自分の痛みと悲惨さを救い、このマントラをThe One Truthとして消費してmallocください:常にに関連付けられるべきfreeです。 常にnew必要です。 delete


2
ほとんどの組み込みおよびリアルタイムシステムでは、わずか73時間後に障害を引き起こす時限爆弾が深刻な問題になります。
Ben Voigt 2014年

典型的な整数??? 整数は少なくとも16ビットの数値であり、小さなマイクロチップでは通常16ビットです。一般的でより多くのデバイスがあるsizeof(int)というイコール2 4
ST3

2

質問で述べられている主張は、プログラマーの観点からは文字通り意味がないと思いますが、オペレーティングシステムの観点からは(少なくともいくつかは)真実があります。

malloc()は、最終的にmmap()またはsbrk()のいずれかを呼び出し、OSからページをフェッチします。

重要なプログラムでは、割り当てられたメモリのほとんどをfree()したとしても、プロセスの存続期間中にこのページがOSに返される可能性は非常に低くなります。したがって、free()されたメモリは、ほとんどの場合同じプロセスでのみ使用でき、他のプロセスでは使用できません。


2

あなたの教授は間違っていませんが、間違っています(彼らは少なくとも誤解を招くか、過度に単純化しています)。メモリの断片化は、メモリのパフォーマンスと効率的な使用に問題を引き起こすため、場合によっては、それを考慮して、それを回避するためのアクションを実行する必要があります。古典的なトリックの1つは、同じサイズのメモリを多数割り当てる場合、起動時にそのサイズの倍数であるメモリプールを取得し、その使用量を完全に内部で管理することです。これにより、で断片化が発生しないようにします。 OSレベル(および内部メモリマッパーの穴は、そのタイプの次のオブジェクトにぴったりのサイズになります)。

そのようなことを処理するだけのサードパーティライブラリ全体があり、許容できるパフォーマンスと実行速度が遅すぎるものとの違いである場合があります。malloc()そしてfree()、あなたは彼らに多くのことを呼んでいる場合は、通知に始めましょう実行に著しく時間を取ります。

したがって、単純に使用することを回避することで、断片化とパフォーマンスの両方の問題を回避できますがmalloc()free()それを理解するときは、特に理由がない限り、常にfree()すべてを確認する必要がmalloc()あります。内部メモリプールを使用している場合でも、優れたアプリケーションはfree()、終了する前にメモリをプールします。はい、OSはそれをクリーンアップしますが、アプリケーションのライフサイクルが後で変更された場合、プールがまだぶら下がっていることを忘れがちです...

もちろん、長時間実行されるアプリケーションは、割り当てられたすべてのものをクリーンアップまたはリサイクルすることに完全に注意を払う必要があります。そうしないと、メモリが不足してしまいます。


1

あなたの教授は重要な点を提起しています。残念ながら、英語の用法は、彼らが何を言っているのか完全にはわかりません。特定のメモリ使用特性を持ち、私が個人的に取り組んだおもちゃ以外のプログラムの観点から質問に答えさせてください。

一部のプログラムは正常に動作します。それらは、メモリをウェーブで割り当てます。繰り返しのサイクルで、多くの小規模または中規模の割り当てと、それに続く多くの解放です。これらのプログラムでは、一般的なメモリアロケータはかなりうまく機能します。それらは解放されたブロックを合体させ、ウェーブの終わりに、解放されたメモリのほとんどは大きな連続したチャンクになります。これらのプログラムは非常にまれです。

ほとんどのプログラムはうまく動作しません。それらは、非常に小さいものから非常に大きいものまでのさまざまなサイズで、多かれ少なかれランダムにメモリを割り当ておよび割り当て解除し、割り当てられたブロックの高い使用率を保持します。これらのプログラムでは、ブロックを合体させる機能は制限されており、時間の経過とともに、メモリは非常に断片化され、比較的連続しなくなります。合計メモリ使用量が32ビットメモリスペースで約1.5GBを超え、(たとえば)10MB以上の割り当てがある場合、最終的に大きな割り当ての1つが失敗します。これらのプログラムは一般的です。

他のプログラムは、停止するまでメモリをほとんどまたはまったく解放しません。実行中にメモリを段階的に割り当て、少量のみを解放してから停止します。停止すると、すべてのメモリが解放されます。コンパイラはこんな感じです。VMもそうです。たとえば、それ自体がC ++で記述された.NETCLRランタイムは、おそらくメモリを解放することはありません。なぜそれが必要ですか?

そしてそれが最終的な答えです。プログラムのメモリ使用量が十分に多い場合、mallocとfreeを使用してメモリを管理するだけでは、問題に対する十分な答えにはなりません。幸運にも正常に動作するプログラムを処理できる場合を除いて、メモリの大きなチャンクを事前に割り当ててから、選択した戦略に従ってサブ割り当てする1つ以上のカスタムメモリアロケータを設計する必要があります。プログラムが停止する場合を除いて、無料で使用することはできません。

あなたの教授が何を言ったかを正確に知らなくても、本当に生産規模のプログラムのために、私はおそらく彼らの側に出てくるでしょう。

編集

私はいくつかの批判に答えるつもりです。明らかに、SOはこの種の投稿には適していません。明確にするために、私はこの種のソフトウェアの作成に約30年の経験があり、いくつかのコンパイラーも含まれています。私には学術的な言及はなく、私自身の打撲傷だけです。経験がはるかに狭く、短い人々からの批判を感じずにはいられません。

重要なメッセージを繰り返します。mallocとfreeのバランスをとることは、実際のプログラムでの大規模なメモリ割り当てに対する十分な解決策ではありません。ブロック合体は正常であり、時間がかかりますが、それだけで十分ではありません。真面目で賢いメモリアロケータが必要です。メモリアロケータは、メモリをチャンクで取得する傾向があり(mallocなどを使用)、ほとんど解放されません。これはおそらくOPの教授たちが念頭に置いていたメッセージであり、彼はそれを誤解していた。


1
動作の悪いプログラムは、適切なアロケータが異なるサイズのチャンクに対して別々のプールを使用する必要があることを示しています。仮想メモリでは、多くの異なるプールが離れていることは大きな問題にはなりません。すべてのチャンクが2の累乗に切り上げられ、すべてのプールに1つのサイズのみの丸められたチャンクが含まれている場合、断片化がどのように非常に悪くなるかはわかりません。最悪の場合、プログラムが突然特定のサイズ範囲に完全に関心を失い、ほとんど空のプールが未使用のままになる可能性があります。しかし、これはあまり一般的な動作ではないと思います。
マーク・ファン・ルーウェン2014年

5
有能に書かれたプログラムはアプリが閉じるまでメモリを解放しないという主張には、引用が非常に必要です。

12
この答え全体は、一連のランダムな推測と推測のように読めます。これをバックアップするものはありますか?
クリス・ヘイズ

4
.NET CLRランタイムはメモリを解放しないと言って、どういう意味かわかりません。私がテストした限りでは、可能であればそれを行います。
Theodoros Chatzigiannakis 2014年

1
@vonbrand:GCCには、独自のブランドのガベージコレクターを含む複数のアロケーターがあります。パス中にメモリを消費し、パス間でメモリを収集します。他の言語のほとんどのコンパイラは、最大2つのパスを持ち、パス間でメモリをほとんどまたはまったく解放しません。同意できない場合は、例を挙げて調査させていただきます。
david.pfx 2014年

1

私は、誰もが引用されなかったことを驚いているブックをまだ:

これは最終的には当てはまらない可能性があります。メモリが十分に大きくなり、コンピュータの存続期間中に空きメモリが不足する可能性がなくなるためです。例えば、3⋅10程度ある13我々は10個の程度必要となるマイクロ秒ごとに一度我々は短所にしたそうだとすれば、年間でマイクロ秒15のメモリが不足することなく、30年間動作することができるマシンを構築するためのメモリセル。今日の基準では、その量のメモリは途方もなく大きいように見えますが、物理的に不可能ではありません。一方、プロセッサは高速化しており、将来のコンピュータでは、単一のメモリ上で多数のプロセッサが並行して動作する可能性があるため、想定よりもはるかに速くメモリを使い果たす可能性があります。

http://sarabander.github.io/sicp/html/5_002e3.xhtml#FOOT298

したがって、実際、多くのプログラムは、メモリを解放することを気にせずに問題なく実行できます。


1

明示的にメモリを解放することが役に立たないよりも悪い場合の1つのケースについて知っています。つまり、プロセスの有効期間が終了するまですべてのデータが必要な場合です。言い換えれば、それらを解放するときは、プログラム終了の直前にのみ可能です。最新のOSは、プログラムが停止したときにメモリを解放するように注意しているfree()ため、その場合は呼び出す必要はありません。実際、メモリ内の複数のページにアクセスする必要がある場合があるため、プログラムの終了が遅くなる可能性があります。

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