web-dev-qa-db-ja.com

参照カウントスマートポインターがなぜそれほど人気が​​あるのですか?

私が見ることができるように、スマートポインターは多くの実際のC++プロジェクトで広く使用されています。

ある種のスマートポインターはRAIIと所有権の転送をサポートするのに明らかに有益ですが、"ガベージコレクション"の方法として、共有ポインターデフォルトではを使用する傾向もあります。プログラマーは割り当てについてそれほど考える必要はありません。

Boehm GC のような適切なガベージコレクターを統合するよりも、共有ポインターの方が人気があるのはなぜですか? (または、実際のGCよりも人気があることに同意しますか?)

参照カウントに対する従来のGCの2つの利点について知っています。

  • 従来のGCアルゴリズムには参照サイクルの問題はありません。
  • 参照カウントは通常、適切なGCよりも遅いです。

参照カウントスマートポインターを使用する理由は何ですか?

52
Miklós Homolya

ガベージコレクションに対する参照カウントのいくつかの利点:

  1. 低オーバーヘッド。ガベージコレクターは非常に煩わしく(たとえば、ガベージコレクションサイクルの処理中に予期しない時間にプログラムをフリーズさせる)、かなりのメモリを消費します(たとえば、ガベージコレクションが最終的に実行される前に、プロセスのメモリフットプリントが不必要に多くのメガバイトに増加します)。

  2. より予測可能な動作。参照カウントを使用すると、最後の参照がなくなるとすぐにオブジェクトが解放されることが保証されます。一方、ガベージコレクションでは、システムがオブジェクトにアクセスしたときに、「いつか」オブジェクトが解放されます。 RAMの場合、これは通常、デスクトップや軽負荷のサーバーでは大きな問題ではありませんが、他のリソース(ファイルハンドルなど)の場合は、後で潜在的な競合を回避するためにできるだけ早く閉じる必要があります。

  3. よりシンプル。参照カウントは数分で説明でき、1〜2時間で実装できます。ガベージコレクター、特にパフォーマンスが適切なものは非常に複雑で、多くの人がそれを理解していません。

  4. 標準。 C++には、参照カウント(shared_ptrを介した)とSTLのフレンドが含まれています。これは、ほとんどのC++プログラマーがそれに慣れており、ほとんどのC++コードがそれで動作することを意味します。ただし、標準のC++ガベージコレクターはありません。つまり、いずれかを選択し、それがユースケースで適切に機能することを望みます。機能しない場合は、言語ではなく、修正するのが問題です。

参照カウントの欠点とされる問題については、サイクルを検出しないことが問題ですが、参照カウントを使用して過去10年間に個人的に遭遇したことはありません。ほとんどのデータ構造は自然に非循環的であり、循環的参照(ツリーノードの親ポインターなど)が必要な状況に遭遇した場合は、「逆方向」にweak_ptrまたは生のCポインターを使用できます。データ構造を設計する際に潜在的な問題を認識している限り、問題はありません。

パフォーマンスに関しては、参照カウントのパフォーマンスに問題があったことはありません。ガベージコレクションのパフォーマンス、特にGCで発生する可能性のあるランダムなフリーズに問題がありました。この問題に対する唯一の解決策(「オブジェクトを割り当てない」)は、「GCを使用しない」と言い換えることもできます。 。

57
Jeremy Friesner

GCから良好なパフォーマンスを得るには、GCがメモリ内のオブジェクトを移動できる必要があります。メモリの場所を直接操作できるC++のような言語では、これはほとんど不可能です。 (Microsoft C++/CLRは、GCで管理されるポインターに新しい構文を導入し、実質的に異なる言語であるため、カウントされません。)

Boehm GCは気の利いたアイデアですが、実際には両方の世界で最悪です。適切なGCよりも遅いmalloc()が必要なため、世代別GCの対応するパフォーマンスの向上なしに、確定的な割り当て/割り当て解除動作が失われます。さらに、それは必然的に保守的であるため、とにかくすべてのゴミを収集する必要はありません。

よく調整された良いGCは素晴らしいことです。しかし、C++のような言語では、利益は最小限であり、コストはそれだけの価値がありません。

しかし、C++ 11がより一般的になるにつれて、ラムダとキャプチャのセマンティクスがC++コミュニティを、LISPコミュニティが最初にGCを発明する原因となったのと同じ種類の割り当てとオブジェクト存続期間の問題へと導き始めるかどうかは興味深いことです。場所。

StackOverflowの関連質問に対する私の回答 も参照してください。

26
Daniel Pryden

私が見ることができるように、スマートポインターは多くの実際のC++プロジェクトで広く使用されています。

確かに、客観的に言えば、コードの大部分は現在、ガベージコレクターをトレースする現代の言語で書かれています。

ある種のスマートポインターはRAIIと所有権の転送をサポートするのに明らかに有益ですが、「ガベージコレクション」の方法としてデフォルトで共有ポインターを使用する傾向もあるため、プログラマーは割り当てについてそれほど考える必要がありません。 。

それでもサイクルについて心配する必要があるので、それは悪い考えです。

共有ポインターは、Boehm GCのような適切なガベージコレクターを統合するよりも人気があるのはなぜですか? (または、実際のGCよりも人気があることに同意しますか?)

ああ、うわぁ、あなたの考え方には多くの問題があります。

  1. ベームのGCは、どのような意味でも「適切な」GCではありません。本当にひどいです。それは保守的であるため、リークし、設計上非効率的です。参照: http://flyingfrogblog.blogspot.co.uk/search/label/boehm

  2. 共有ポインタは、客観的にはGCほど人気が​​ありません。開発者の大多数が現在GCの言語を使用しており、共有ポインタを必要としないためです。 JavaおよびC++と比較した求人市場のJavascriptを見てください。

  3. GCは接線の問題であると思うので、あなたはC++への考慮を制限しているように見えます。まともなGCを取得する(only方法は、言語を設計することではありませんVMはじめに)選択バイアスを導入しています本当に適切なガベージコレクションを望んでいる人はC++に固執しません。

参照カウントスマートポインターを使用する理由は何ですか?

C++に制限されていますが、自動メモリ管理が必要です。

4
Jon Harrop

MacOS XとiOS、およびObjective-CまたはSwiftを使用する開発者では、参照カウントは自動的に処理されるため人気があり、ガベージコレクションの使用はAppleをサポートしていないため、大幅に減少しています。 (ガベージコレクションを使用するアプリは次のMacOS Xバージョンで機能しなくなり、ガベージコレクションはiOSに実装されなかったと言われています。)ガベージコレクションを使用するソフトウェアがたくさんあったのに、それが利用可能になったときは、本当に多くの疑いがありました。

ガベージコレクションを削除する理由:ガベージコレクターがアクセスできない領域へのポインターが「エスケープ」できるCスタイルの環境では、確実に機能しませんでした。 Apple強く信じられており、参照カウントがより高速であると信じています(ここで相対速度について主張することはできますが、Appleを説得することはできませんでした)。そして結局、誰もガベージコレクションを使用していません。

MacOS XまたはiOS開発者が最初に学ぶことは、参照サイクルの処理方法です。そのため、実際の開発者にとっては問題ではありません。

3
gnasher729

C++でのガベージコレクションの最大の欠点は、正しく実行することが明らかに不可能であることです。

  • C++では、ポインターは独自のウォールドコミュニティに存在せず、他のデータと混合されます。そのため、有効なポインターとして解釈できるビットパターンを持つ他のデータとポインターを区別することはできません。

    結果:すべてのC++ガベージコレクターは、収集する必要があるオブジェクトをリークします。

  • C++では、ポインター演算を行ってポインターを導出できます。そのため、ブロックの先頭へのポインタが見つからない場合でも、そのブロックを参照できないことを意味するわけではありません。

    結果:すべてのC++ガベージコレクターは、これらの調整を考慮に入れて、ブロックの終わりの直後を含む、ブロック内の任意の場所を指すビットシーケンスを、ブロックを参照する有効なポインターとして扱う必要があります。

    注:次のようなトリックでコードを処理できるC++ガベージコレクターはありません。

    int* array = new int[7];
    array--;    //undefined behavior, but people may be tempted anyway...
    for(int i = 1; i <= 7; i++) array[i] = i;
    

    確かに、これは未定義の動作を呼び出します。ただし、既存のコードの中には、適切なコードよりも優れているものがあり、ガベージコレクターによる予備的な割り当て解除をトリガーする可能性があります。