C ++のゴミはどうなりますか?


51

Javaには自動GCがあり、たまに世界を停止しますが、ヒープ上のゴミを処理します。現在、C / C ++アプリケーションにはこれらのSTWフリーズがなく、メモリ使用量も無限に増加しません。この動作はどのように達成されますか?死んだオブジェクトはどのように処理されますか?


38
注:stop-the-worldは、一部のガベージコレクターの実装選択ですが、すべてではありません。たとえば、ミューテーターと同時に実行される並行GCがあります(GC開発者が実際のプログラムと呼んでいるものです)。IBMのオープンソースJVM J9の商用バージョンを購入でき、コンカレントポーズレスコレクターがあると思います。Azul Zingには「一時停止なし」のコレクターがあり、実際には一時停止しないが非常に高速であるため、顕著な一時停止はありません(GCの一時停止はオペレーティングシステムのスレッドコンテキストスイッチと同じ順序で、通常一時停止とは見なされません) 。
ヨルグWミットタグ

14
私が使用(長時間実行)C ++プログラムのほとんどはやる際限以上の時間を成長メモリ使用量を持っています。一度に数日以上プログラムを開いたままにしておく習慣がないのでしょうか?
ジョナサンキャスト

12
スマートポインターを使用して動的メモリを管理できるため、最新のC ++とその構造では、メモリを手動で削除する必要がなくなります(特別な最適化を行っている場合を除きます)。明らかに、C ++開発にいくらかのオーバーヘッドが追加されるため、もう少し注意する必要がありますが、まったく異なることではなく、単にmanualを呼び出す代わりにスマートポインター構造を使用することを忘れないでくださいnew
アンディ

9
ガベージコレクションされた言語でメモリリークが発生する可能性があることに注意してください。私はJavaには不慣れですが、残念ながら、.NETの管理されたGCの世界ではメモリリークがよく発生します。静的フィールドによって間接的に参照されるオブジェクトは自動的に収集されず、イベントハンドラーは非常に一般的なリークのソースであり、ガベージコレクションの非決定的な性質により、リソースを手動で解放する必要性を完全になくすことはできません(IDisposableパターン)。すべては、適切に使用されるC ++メモリ管理モデルはガベージコレクションよりもはるかに優れています。
コーディグレイ

26
What happens to garbage in C++? 通常、実行可能ファイルにコンパイルされていませんか?
BJマイヤーズ

回答:


100

プログラマは、を介して作成したオブジェクトがを介しnewて削除されるようにする責任がありますdelete。オブジェクトが作成されたが、最後のポインターまたはオブジェクトへの参照が範囲外になる前に破壊されなかった場合、そのオブジェクトはクラックを通過してメモリリークになります。

残念ながら、C、C ++、およびGCを含まない他の言語では、これは単純に時間の経過とともに積み上げられます。アプリケーションまたはシステムのメモリが不足し、新しいメモリブロックを割り当てることができなくなる可能性があります。この時点で、ユーザーはオペレーティングシステムがその使用済みメモリを再利用できるように、アプリケーションを終了する必要があります。

この問題を軽減する限り、プログラマーの生活をずっと楽にするいくつかのことがあります。これらは主に範囲の性質によってサポートされます

int main()
{
    int* variableThatIsAPointer = new int;
    int variableInt = 0;

    delete variableThatIsAPointer;
}

ここでは、2つの変数を作成しました。それらは、中括弧で定義されているように、ブロックスコープに存在し{}ます。実行がこのスコープ外に移動すると、これらのオブジェクトは自動的に削除されます。この場合、variableThatIsAPointerその名前が示すように、はメモリ内のオブジェクトへのポインターです。範囲外になると、ポインターは削除されますが、ポインターが指すオブジェクトは残ります。ここでdeleteは、メモリリークがないことを確認するために、このオブジェクトがスコープ外になる前にこのオブジェクトを使用します。ただし、このポインタを他の場所に渡し、後で削除されることを期待することもできます。

このスコープの性質はクラスにまで及びます:

class Foo
{
public:
    int bar; // Will be deleted when Foo is deleted
    int* otherBar; // Still need to call delete
}

ここでも同じ原理が適用されます。がbarいつFoo削除されるかを心配する必要はありません。ただし、otherBarでは、ポインターのみが削除されます。場合はotherBar、それが指すものは何でもオブジェクトへの唯一の有効なポインタである、我々はおそらくべきdeleteで、それFooデストラクタさん。これがRAIIの背後にある運転コンセプトです

リソースの割り当て(取得)は、オブジェクトの作成(具体的には初期化)中にコンストラクタによって行われ、リソースの割り当て解除(解放)は、オブジェクトの破棄(具体的にはファイナライズ)の間、デストラクタによって行われます。したがって、リソースは、初期化が完了してからファイナライズが開始されるまでの間(リソースの保持はクラス不変)に保持され、オブジェクトが生きている場合にのみ保持されることが保証されます。したがって、オブジェクトリークがない場合、リソースリークはありません。

RAIIは、スマートポインターの背後にある典型的な原動力でもあります。C ++標準ライブラリでは、これらはstd::shared_ptrstd::unique_ptr、とstd::weak_ptr。私は同じ概念に従う他のshared_ptr/ weak_ptr実装を見て使用しましたが。これらの場合、参照カウンターは、指定されたオブジェクトへのポインターの数を追跡しdelete、オブジェクトへの参照がなくなると自動的にオブジェクトをsします。

それを超えると、プログラマーがコードがオブジェクトを適切に処理することを保証するための適切な実践と規律にすべて帰着します。


4
経由で削除delete-それは私が探していたものです。驚くばかり。
ジュシュア

3
c ++で提供されるスコープメカニズムについて追加すると、ほとんどの新規および削除をほぼ自動化できます。
whatsisname

9
@whatsisnameそれは、彼らが多くの場合、まったく発生しないということで、新たに、削除が自動行われることではありません
Caleth

10
delete自動であなたのために呼び出されたスマートポインタをあなたがそれらを使用する場合、自動ストレージが使用できない場合、あなたがそれらを毎回使用することを検討する必要がありますので。
マリアンスパニック

11
@JuShua最新のC ++を記述するときdelete、アプリケーションコード(およびC ++ 14以降、と同じnew)に実際に含める必要はありませんが、代わりにスマートポインターとRAIIを使用してヒープオブジェクトを削除する必要があります。std::unique_ptr型とstd::make_unique関数は、アプリケーションコードレベルの直接newかつ最も単純な置換deleteです。
ハイド

82

C ++にはガベージコレクションがありません。

C ++アプリケーションは、ごみを処理する必要があります。

これを理解するには、C ++アプリケーションプログラマが必要です。

彼らが忘れるとき、結果は「メモリリーク」と呼ばれます。


22
あなたは確かにあなたの答えにゴミも定型も含まれていないことを確認しました
...-leftaroundabout

15
@leftaroundabout:ありがとう。ほめ言葉だと思います。
ジョンR.ストローム

1
[OK]このガベージフリーの回答には、検索するキーワードがあります:メモリリーク。また、何らかの形で言及することは素敵なことだnewdelete
ルスラン

4
同じことがまた、適用される@Ruslan mallocおよびfree、またはnew[]およびdelete[]、またはその他のアロケータ(のWindowsのようにGlobalAllocLocalAllocSHAllocCoTaskMemAllocVirtualAllocHeapAlloc、...)、そしてあなたのために割り当てられたメモリ(例えば経由fopen)。
user253751

43

C、C ++、およびガベージコレクターのない他のシステムでは、開発者は言語とそのライブラリによって機能を提供され、メモリをいつ再利用できるかを示します。

最も基本的な機能は自動ストレージです。多くの場合、言語自体がアイテムの破棄を保証します:

int global = 0; // automatic storage

int foo(int a, int b) {
    static int local = 1; // automatic storage

    int c = a + b; // automatic storage

    return c;
}

この場合、コンパイラーは、それらの値がいつ使用されないかを把握し、それらに関連付けられたストレージを再利用します。

Cで動的ストレージを使用する場合、メモリは伝統的にで割り当てられmalloc、再利用されfreeます。C ++では、メモリは伝統的にで割り当てられnew、再利用されdeleteます。

Cはここ数年あまり変わっていませんが、現代のC ++ は完全に廃止しnewdelete代わりにライブラリ機能(それ自体が適切に使用newdeleteます)に依存しています。

  • スマートポインタは最も有名である:std::unique_ptr及びstd::shared_ptr
  • しかし、コンテナは実際にははるかに普及している:std::stringstd::vectorstd::map、...すべて内部的に透過的に動的に割り当てられたメモリを管理します

と言えばshared_ptr、リスクがあります。参照のサイクルが形成され、壊れていない場合、メモリリークが発生する可能性があります。この状況を回避するのは開発者次第であり、最も単純な方法はshared_ptr完全に回避することであり、2番目に単純な方法は型レベルでのサイクルを回避することです。

その結果、メモリリークは、C ++での問題ではない限り、彼らは使用を控えて、でも新しいユーザーのために、newdeleteまたはstd::shared_ptr。これは、堅固な規律が必要なCとは異なり、通常は不十分です。


ただし、メモリリークの双子の姉妹、ダングリングポインターについて言及しなければ、この答えは完全ではありません。

宙ぶらりんのポインター(または宙ぶらりんの参照)は、死んだオブジェクトへのポインターまたは参照を保持することによって作成される危険です。例えば:

int main() {
    std::vector<int> vec;
    vec.push_back(1);     // vec: [1]

    int& a = vec.back();

    vec.pop_back();       // vec: [], "a" is now dangling

    std::cout << a << "\n";
}

ダングリングポインターまたは参照の使用は、未定義の動作です。一般的に、幸いなことに、これは即時のクラッシュです。非常に頻繁に、残念ながら、これは最初にメモリ破損を引き起こします...そしてコンパイラが本当に奇妙なコードを出力するため、時々奇妙な動作が発生します。

未定義の動作は、プログラムのセキュリティ/正確性の点で、今日までのCおよびC ++の最大の問題です。ガベージコレクターも未定義の動作もない言語のRustをチェックすることをお勧めします。


17
再:「ぶら下がりポインタまたは参照を使用すると、未定義の動作になります。一般的に、幸いなことに、これは即時クラッシュです」:本当ですか?それは私の経験とはまったく一致しません。それどころか、私の経験では、ダングリングポインターを使用して即座にクラッシュすることはほとんどありません。。。
ruakh

9
ええ、「ダングリング」するために、ポインターは以前に割り当てられたメモリをある時点でターゲットにしている必要があり、そのメモリは通常、プロセスから完全にマップ解除されていないため、アクセスできなくなります。すぐに再利用するのに適した候補です。実際には、ぶら下がりポインタはクラッシュを引き起こさず、カオスを引き起こします。
ルーシェンコ

2
「その結果、メモリリークはC ++の問題ではありません。」確かに、ライブラリへのCバインディングや、再帰的なshared_ptrsまたは再帰的なunique_ptrsなどの状況が常にあります。
Mooingダック

3
「新しいユーザーであってもC ++の問題ではない」–私はそれを「Javaのような言語やCから来ていない新しいユーザー」に限定します。
左回り

3
@leftaroundabout:「newdeleteおよびshared_ptr」の使用を控えている限り、それは修飾されています。なしでnewshared_ptrあなたは直接所有権を持っているので、漏れはありません。もちろん、あなたはぶら下がりポインタなどを持っている可能性があります...しかし、私はあなたがそれらを取り除くためにC ++を残す必要があるのではないかと心配しています。
マチューM.

27

C ++にはRAIIと呼ばれるものがあります。基本的には、ゴミは山に置いておくのではなく、ゴミを片付けて掃除機で片付けます。(私の部屋でサッカーを見ながら想像してください-ビールの缶を飲んで新しいものが必要なとき、C ++の方法は空の缶を冷蔵庫に行く途中でビンに持って行くことです。C#の方法は床にそれをチャックすることです。彼女が掃除をするようになると、メイドがそれらを拾うのを待ちます)。

現在、C ++でメモリリークが発生する可能性がありますが、そのためには、通常の構造をそのままにして、Cの方法に戻る必要があります。メモリのブロックを割り当て、言語の支援なしでそのブロックの場所を追跡します。一部の人々はこのポインタを忘れてしまい、ブロックを削除できません。


9
共有ポインター(RAIIを使用)は、リークを作成する最新の方法を提供します。オブジェクトAとBが共有ポインターを介して相互に参照し、オブジェクトAまたはオブジェクトBを参照するものが他にないとします。結果はリークです。この相互参照は、ガベージコレクションを使用する言語では問題になりません。
デビッドハンメン

@DavidHammenは確かですが、確認するためにほぼすべてのオブジェクトを走査するコストがかかります。スマートポインターの例では、スマートポインター自体が範囲外になり、オブジェクトが解放されるという事実を無視します。スマートポインターは、ほとんどのパラメーターのようにスタック上で渡されるオブジェクトではなく、ポインターのようなものであると仮定します。これは、GC言語で発生するメモリリークと大差ありません。たとえば、UIクラスからイベントハンドラーを削除すると、静かに参照されるためリークが発生する有名なものです。
gbjbaanb

1
スマートポインタを持つ例で@gbjbaanb、スマートポインタでもないが、これまでのリークがありますなぜだと、スコープ外になります。両方のスマートポインターオブジェクトは、レキシカルオブジェクトではなく動的スコープに割り当てられているため、破壊する前にそれぞれ他のオブジェクトを待機しようとします。スマートポインターがC ++の実際のオブジェクトであり、ポインターだけではないという事実は、まさにここでリークを引き起こすものです- コンテナオブジェクトを指しているスタックスコープ内の追加のスマートポインターオブジェクトは、refcountゼロ以外。
ルーシェンコ

2
.NETの方法は、床にチャックすることではありません。メイドがやってくるまで、それを元の場所に保持します。また、.NETが実際に(契約ではなく)メモリを割り当てる方法により、ヒープはランダムアクセススタックに似ています。それは一種の契約書や書類を持っているようなもので、時々それを通過して、もはや有効ではないものを破棄します。これを簡単にするために、各破棄で生き残ったものは異なるスタックに昇格されるため、ほとんどすべてのスタックを横断することを避けることができます-最初のスタックが十分に大きくならない限り、メイドは他のスタックに触れません。
ルアーン

@Luaanそれは類推でした...メイドが掃除するまでテーブルの上に缶を置いておくと言ったらもっと幸せになると思います。
-gbjbaanb

26

C ++の場合、「手動のメモリ管理を行う必要がある」という一般的な誤解であることに注意してください。実際、通常はコード内でメモリ管理を行いません。

固定サイズのオブジェクト(スコープ有効期間付き)

オブジェクトが必要なほとんどの場合、オブジェクトはプログラム内で定義された有効期間を持ち、スタック上に作成されます。これは、すべての組み込みプリミティブデータ型で機能しますが、クラスおよび構造体のインスタンスでも機能します。

class MyObject {
    public: int x;
};

int objTest()
{
    MyObject obj;
    obj.x = 5;
    return obj.x;
}

関数が終了すると、スタックオブジェクトは自動的に削除されます。Javaでは、オブジェクトは常にヒープ上に作成されるため、ガベージコレクションなどの何らかのメカニズムで削除する必要があります。これは、スタックオブジェクトの問題ではありません。

動的データを管理するオブジェクト(スコープ有効期間付き)

スタック上のスペースの使用は、固定サイズのオブジェクトに対して機能します。配列などの可変量のスペースが必要な場合は、別のアプローチが使用されます。リストは、動的メモリを管理する固定サイズのオブジェクトにカプセル化されます。オブジェクトが特別なクリーンアップ機能であるデストラクタを持つことができるため、これは機能します。オブジェクトがスコープ外に出て、コンストラクターの反対を行うときに呼び出されることが保証されています。

class MyList {        
public:
    // a fixed-size pointer to the actual memory.
    int* listOfInts; 
    // constructor: get memory
    MyList(size_t numElements) { listOfInts = new int[numElements]; }
    // destructor: free memory
    ~MyList() { delete[] listOfInts; }
};

int listTest()
{
    MyList list(1024);
    list.listOfInts[200] = 5;
    return list.listOfInts[200];
    // When MyList goes off stack here, its destructor is called and frees the memory.
}

メモリが使用されるコードには、メモリ管理がまったくありません。確認する必要があるのは、作成したオブジェクトに適切なデストラクタがあることだけです。のスコープをどのように抜けlistTestても、例外を介して、または単にそこから戻ることによって、デストラクタ~MyList()が呼び出され、メモリを管理する必要はありません。

(デストラクタを示すために、バイナリNOT演算子を使用するのはおかしい設計上の決定だと思います~。数値で使用する場合、ビットを反転します。

基本的に、動的メモリを必要とするすべてのC ++オブジェクトは、このカプセル化を使用します。これはRAII(「リソースの取得は初期化」)と呼ばれています。これは、オブジェクトが自分のコンテンツを気にするという単純な考えを表現する非常に奇妙な方法です。彼らが取得するものは、クリーンアップするためのものです。

ポリモーフィックオブジェクトとスコープを超えたライフタイム

現在、これらのケースは両方とも、明確に定義されたライフタイムを持つメモリ用でした:ライフタイムはスコープと同じです。スコープを離れるときにオブジェクトの有効期限が切れないようにするには、メモリを管理できる3番目のメカニズム、スマートポインターがあります。スマートポインターは、実行時に型が異なるが、共通のインターフェイスまたは基本クラスを持つオブジェクトのインスタンスがある場合にも使用されます。

class MyDerivedObject : public MyObject {
    public: int y;
};
std::unique_ptr<MyObject> createObject()
{
    // actually creates an object of a derived class,
    // but the user doesn't need to know this.
    return std::make_unique<MyDerivedObject>();
}

int dynamicObjTest()
{
    std::unique_ptr<MyObject> obj = createObject();
    obj->x = 5;
    return obj->x;
    // At scope end, the unique_ptr automatically removes the object it contains,
    // calling its destructor if it has one.
}

std::shared_ptr複数のクライアント間でオブジェクトを共有するための別の種類のスマートポインターがあります。最後のクライアントがスコープから出たときに含まれるオブジェクトのみを削除するため、クライアントの数とオブジェクトの使用期間が完全に不明な状況で使用できます。

要約すると、手動のメモリ管理は実際には行わないことがわかります。すべてがカプセル化され、完全に自動化されたスコープベースのメモリ管理によって処理されます。これでは不十分な場合、生メモリをカプセル化するスマートポインターが使用されます。

C ++コードのどこでも、リソースオーナーとして生のポインターを使用すること、コンストラクターの外部での生の割り当てdelete、デストラクタの外部での生の呼び出しを使用することは、非常に悪い習慣と見なされます。例外が発生した場合、それらを管理することはほとんど不可能であり、一般に安全に使用することは困難です。

最適:これはすべてのタイプのリソースで機能します

RAIIの最大の利点の1つは、メモリに限定されないことです。実際には、ファイルやソケット(オープン/クローズ)などのリソースと、相互排他ロック(ロック/ロック解除)などの同期メカニズムを管理する非常に自然な方法を提供します。基本的に、取得可能で解放する必要のあるすべてのリソースは、C ++でまったく同じ方法で管理され、この管理はユーザーに委ねられません。すべてコンストラクターで取得し、デストラクタで解放するクラスにカプセル化されます。

たとえば、ミューテックスをロックする関数は通常、C ++で次のように記述されます。

void criticalSection() {
    std::scoped_lock lock(myMutex); // scoped_lock locks the mutex
    doSynchronizedStuff();
} // myMutex is released here automatically

他の言語では、これを手動で行う必要がある(たとえば、finally句で)か、この問題を解決する特別なメカニズムが生成されますが、特にエレガントな方法ではありません(通常、十分な人々が欠点に苦しんだ)。このようなメカニズムは、Javaのtry-with-resourcesおよびC#のusingステートメントであり、どちらもC ++のRAIIの近似です。

要約すると、これはすべてC ++のRAIIの非常に表面的な説明でしたが、C ++のメモリやリソース管理も通常は「手動」ではなく、実際はほとんど自動であることを読者が理解できるように願っています。


7
これは、人々を誤解させたり、C ++を実際よりも困難または危険にしたりしない唯一の答えです。
アレクサンドル・レボ

6
ところで、生のポインタをリソースの所有者として使用することは悪い習慣と考えられています。ポインター自体よりも長持ちすることが保証されているものを指し示していれば、それらを使用しても何も問題はありません。
アレクサンドル・レボ

8
2番目のアレキサンダー。「C ++には自動メモリ管理がなく、Aを忘れてdelete、あなたは死んでいる」という答えが30ポイントを超えて急上昇し、受け入れられていますが、これには5つあります。誰もが実際にここでC ++を使用していますか?
クエンティン

8

特にCに関しては、この言語は動的に割り当てられたメモリを管理するツールを提供しません。すべて*allocに対応するfree場所があることを確認するのは絶対に責任があります。

本当に厄介なのは、リソースの割り当てが途中で失敗したときです。もう一度やり直しますか?ロールバックして最初からやり直しますか?ロールバックしてエラーで終了しますか?完全に救済してOSに対処させますか?

たとえば、次は非連続2D配列を割り当てる関数です。ここでの動作は、プロセスの途中で割り当てエラーが発生した場合、すべてをロールバックし、NULLポインターを使用してエラー表示を返します。

/**
 * Allocate space for an array of arrays; returns NULL
 * on error.
 */
int **newArr( size_t rows, size_t cols )
{
  int **arr = malloc( sizeof *arr * rows );
  size_t i;

  if ( arr ) // malloc returns NULL on failure
  {
    for ( i = 0; i < rows; i++ )
    {
      arr[i] = malloc( sizeof *arr[i] * cols );
      if ( !arr[i] )
      {
        /**
         * Whoopsie; we can't allocate any more memory for some reason.
         * We can't just return NULL at this point since we'll lose access
         * to the previously allocated memory, so we branch to some cleanup
         * code to undo the allocations made so far.  
         */
        goto cleanup;
      }
    }
  }
  goto done;

/**
 * We encountered a failure midway through memory allocation,
 * so we roll back all previous allocations and return NULL.
 */
cleanup:
  while ( i )         // this is why we didn't limit the scope of i to the for loop
    free( arr[--i] ); // delete previously allocated rows
  free( arr );        // delete arr object
  arr = NULL;

done:
  return arr;
}

このコードは、バット醜いものとgotoこれはちょうど、完全に救済することなく、かなりの問題に対処する唯一の方法である、存在しない状態で、構造化例外処理メカニズムの任意の並べ替えのが、特にあなたの資源配分コードがネストされている場合より1ループよりも深く。これは、goto実際に魅力的なオプションである非常にまれな時間の1つです。それ以外の場合は、一連のフラグと余分なifステートメントを使用しています。

次のようなリソースごとに専用のアロケータ/デアロケータ関数を書くことで、自分自身の生活を楽にすることができます。

Foo *newFoo( void )
{
  Foo *foo = malloc( sizeof *foo );
  if ( foo )
  {
    foo->bar = newBar();
    if ( !foo->bar ) goto cleanupBar;
    foo->bletch = newBletch(); 
    if ( !foo->bletch ) goto cleanupBletch;
    ...
  }
  goto done;

cleanupBletch:
  deleteBar( foo->bar );
  // fall through to clean up the rest

cleanupBar:
  free( foo );
  foo = NULL;

done:
  return foo;
}

void deleteFoo( Foo *f )
{
  deleteBar( f->bar );
  deleteBletch( f->bletch );
  free( f );
}

1
これは、gotoステートメントがあっても良い答えです。これは、一部の領域で推奨されるプラクティスです。これは、Cの例外に相当するものから保護するために一般的に使用されるスキームです。Linuxカーネルコードを見てくださいgoto
デビッドハンメン

「完全に救済することなく」->公平に、Cについて話したい場合、これはおそらく良い習慣です。Cは最高のいずれかがどこかから来たメモリのブロックを取り扱い、使用する言語であるか、他の手順にメモリの小さな塊を個別の、しかし好ましくは、インターリーブの方法で両方を同時に行っていません。Cで古典的な「オブジェクト」を使用している場合、おそらくその言語を長所に使用していないでしょう。
ルーシェンコ

2番目gotoは無関係です。およびに変更goto done;するreturn arr;と読みやすくなります。より複雑なケースでは、実際には複数のs が存在する可能性がありますが、異なるレベルの準備状態で展開し始めます(C ++での例外スタックのアンワインドによって行われること)。arr=NULL;done:return arr;return NULL;goto
ルスラン

2

私は、記憶の問題をさまざまなカテゴリーに分類することを学びました。

  • 一度滴る。プログラムが起動時に100バイトをリークし、二度とリークしないと仮定します。これらの1回限りのリークを追いかけて排除するのは良いことです(リーク検出機能によってクリーンなレポートを作成するのが好きです)が、必須ではありません。時には、攻撃する必要がある大きな問題があります。

  • 繰り返しの漏れ。プログラムの有効期間中に繰り返し呼び出される関数で、定期的にメモリをリークすることは大きな問題です。これらのしずくは、プログラムと、場合によってはOSを拷問して死に至らしめるでしょう。

  • 相互参照。オブジェクトAとBが共有ポインターを介して相互に参照する場合、それらのクラスの設計、またはこれらのクラスを実装/使用して循環性を破るコードのいずれかで、特別なことを行う必要があります。(これは、ガベージコレクションされた言語の問題ではありません。)

  • 覚えすぎ。これは、ガベージ/メモリリークの邪悪な従兄弟です。RAIIはここでは役に立ちませんし、ガベージコレクションも行いません。これはどの言語でも問題です。アクティブな変数に、ランダムなメモリチャンクに接続する経路がある場合、そのメモリのランダムチャンクはガベージではありません。プログラムを忘れっぽくして数日間実行できるようにするのは難しい。数か月間(たとえば、ディスクが故障するまで)実行できるプログラムを作成するのは、非常に難しい作業です。

私は長い間、リークに関して深刻な問題を抱えていませんでした。C ++でRAIIを使用すると、これらのしずくと漏れに対処するのに非常に役立ちます。(しかし、共有ポインターには注意する必要があります。)さらに重要なことに、メモリの使用が不要になったため、メモリへの接続が切断されるため、メモリの使用量が増え続けます。


-6

必要に応じて独自の形式のガベージコレクションを実装するのは、C ++プログラマ次第です。そうしないと、いわゆる「メモリリーク」が発生します。「Java」などの「高レベル」言語ではガベージコレクションが組み込まれていますが、CやC ++などの「低レベル」言語では組み込まれていません。

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