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


52

ご覧のとおり、スマートポインターは多くの実際のC ++プロジェクトで広く使用されています。

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

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

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

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

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


6
これを使用するのが間違っているデフォルトであるというコメントを追加したいだけです。ほとんどの場合、これstd::unique_ptrで十分であり、実行時のパフォーマンスの観点から、生のポインタよりもオーバーヘッドがゼロです。std::shared_ptrどこでも使用することで、所有権のセマンティクスもあいまいになり、自動リソース管理以外のスマートポインターの主な利点の1つが失われます-コードの背後にある意図を明確に理解します。
マット

2
申し訳ありませんが、ここで受け入れられた答えは完全に間違っています。参照カウントにはオーバーヘッドが大きく(マークビットの代わりにカウントされ、実行時のパフォーマンスが低下します)、雪崩が減少する際の無制限の休止時間があり、チェイニーのセミスペースなどの複雑なものはありません。
ジョンハロップ

回答:


57

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

  1. 低オーバーヘッド。ガベージコレクターは非常に邪魔になり(たとえば、ガベージコレクションサイクルの処理中に予測不能な時間にプログラムがフリーズする)、非常にメモリ集約型です(たとえば、ガベージコレクションが最終的に開始される前にプロセスのメモリフットプリントが不必要に数メガバイトに増加する)

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

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

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

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

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


16
単純な参照カウントの実装では、通常、待機時間を犠牲にして運用GCよりもはるかに低いスループット(30〜40%)が得られます。ギャップは、refcountに使用するビット数を減らしたり、エスケープするまでオブジェクトの追跡を回避するなどの最適化で埋めることができます。C++は、主make_sharedに戻るときにこれを自然に行います。それでも、リアルタイムアプリケーションではレイテンシがより大きな問題になる傾向がありますが、一般的にスループットがより重要であるため、GCのトレースが広く使用されています。私は彼らのことをひどく話すのはそんなに早くないでしょう。
ジョンパーディ

3
面で単純:私は「単純」を屁理屈したい実装の総量に必要なことをコードのはい、しかし単純ではない用途は「を実行(RCを使用する方法を誰かに言っ比較:それはこれをオブジェクトと作成するとき、これをそれらを破壊するとき」を)GC( '...')を使用する方法(単純に、多くの場合十分です)
AakashM

4
「参照カウントを使用すると、オブジェクトへの最後の参照がなくなるとすぐにオブジェクトが解放されることが保証されます」。それはよくある誤解です。flyingfrogblog.blogspot.co.uk/2013/10/...
ジョン・ハロップ

4
@JonHarrop:そのブログ投稿はひどく間違っている。すべてのコメント、特に最後のコメントも読む必要があります。
デデュプリケーター

3
@JonHarrop:はい、あります。彼は、生涯が閉じ括弧に至る完全な範囲であることを理解していません。また、変数が再び使用されない場合、コメントによるとたまにしか機能しないF#の最適化は、ライフタイムを早く終了させます。当然、独自の危険があります。
デュプリケータ

26

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

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

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

しかし、C ++ 11の人気が高まるにつれて、ラムダとキャプチャセマンティクスがC ++コミュニティを最初のLispコミュニティにGCを発明させたのと同じ種類の割り当てとオブジェクトライフタイムの問題に導くかどうかは興味深いでしょう。場所。

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


6
Boehm GCについて、私は時折、一般的なテクノロジーの悪い第一印象を与えることによって、CおよびC ++プログラマーの間で従来のGCに対する嫌悪感に個人的にどれだけ責任があるのか疑問に思いました。
ルーシェンコ

@Leushenkoまあ言った。適切な例は、Boehm gcが「適切な」gcと呼ばれるこの質問です。これは遅く、事実上リークが保証されているという事実を無視します。shared_ptrにpythonスタイルのサイクルブレーカーを実装しているかどうかを調査中にこの質問を見つけました。これは、c ++実装の価値ある目標のように思えます。
user4815162342

4

ご覧のとおり、スマートポインターは多くの実際のC ++プロジェクトで広く使用されています。

確かに、客観的には、コードの大部分は、ガベージコレクターをトレースする最新の言語で記述されています。

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

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

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

おお、すごい、あなたの考え方に多くの間違いがあります:

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

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

  3. GCが接線的な問題だと思うので、C ++への考慮を制限しているように見えます。そうではない(まともなGCを取得する唯一の方法は、最初から言語とVMを設計することです)ので、選択バイアスを導入しています。適切なガベージコレクションが本当に必要な人は、C ++に固執しません。

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

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


7
あの、それは質問がタグ付けされたのですC ++ C ++の機能について語っています。明らかに、一般的なステートメントはすべて、プログラミング全体ではなく、C ++コード内で言及されています。したがって、「客観的に」ガベージコレクションはC ++の世界以外で使用されている可能性があります。これは、最終的には当面の質問とは無関係です。
ニコルボーラス

2
最後の行は明らかに間違っています。C++を使用しているため、GCに対処する必要がなく、リソースの解放が遅れていることを嬉しく思います。AppleがGCを好まない理由があり、GCを使用する言語の最も重要なガイドラインは次のとおりです。アイドル状態のリソースを大量に持っているか、役に立たない限り、ゴミを作成しないでください。
デデュプリケーター

3
@JonHarrop:それで、GCがある場合とない場合の同等の小規模なプログラムを比較してください。あなたはどちらがより多くのメモリを必要とすると思いますか?

1
@Deduplicator:どちらかの結果をもたらすプログラムを思い描くことができます。ジェネレーションGCの病理学的パフォーマンスであり、ほとんどのフローティングガベージが生成されるため、ナーサリ(リストのキューなど)を生き残るまでヒープ割り当てメモリを維持するようにプログラムが設計されている場合、参照カウントはGCのトレースよりも優れています。ガベージコレクションのトレースは、多くの小さなオブジェクトがあり、存続期間が短いが静的によく知られていないため、純粋に機能的なデータ構造を使用するロジックプログラムのようなスコープベースの参照カウントよりも少ないメモリで済みます。
ジョンハロップ

3
@JonHarrop:C ++を話す場合、GC(トレースなど)とRAIIを使用します。これには参照カウントが含まれますが、有用な場合のみです。または、Swiftプログラムと比較できます。
デデュプリケーター

3

MacOS XとiOS、およびObjective-CまたはSwiftを使用する開発者では、参照カウントは自動的に処理されるため人気があり、ガベージコレクションの使用はAppleがもうサポートしていないためかなり減少しています(使用しているアプリはガベージコレクションは次のMacOS Xバージョンで破損し、ガベージコレクションはiOSで実装されませんでした。ガベージコレクションが使用可能になったときに、ガベージコレクションを使用するソフトウェアがこれまでにたくさんあったことを、私は実際に真剣に疑っています。

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

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


私がそれを理解する方法は、物事を決定したのはCのような環境ではなかったが、GCは不確定であり、許容可能なパフォーマンスを得るためにより多くのメモリを必要とし、外部のサーバー/デスクトップは常に少し不足しているということでした。
デュプリケータ

ガベージコレクタが(クラッシュにつながる)私はまだ使用していたオブジェクトを破壊した理由をデバッグすることを決めた私のため :-)
gnasher729

ええ、そうです。最後にその理由を見つけましたか?
デデュプリケーター

はい、これはコールバック関数で返される「コンテキスト」としてvoid *を渡す多くのUnix関数の1つでした。void *は実際にはObjective-Cオブジェクトであり、ガベージコレクターは、Unix呼び出しでオブジェクトが隠されていることに気付きませんでした。コールバックが呼び出され、void *をObject *にキャストします、kaboom!
gnasher729

2

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;

    確かに、これは未定義の動作を呼び出します。ただし、既存のコードの中には、それよりも優れたものがあり、ガベージコレクターによる事前の割り当て解除をトリガーする場合があります。


2
他のデータと混合されています。」他のデータと「混合」されているのはそれほどではありません。C ++型システムを使用して、ポインターとそうでないものを簡単に確認できます。問題は、ポインターが頻繁他のデータになることです。ポインタを整数に非表示にすることは、多くのCスタイルAPIにとって残念な一般的なツールです。
ニコルボーラス

1
c ++でガベージコレクターを台無しにするために、未定義の動作さえ必要としません。たとえば、ファイルへのポインタをシリアル化し、後で読み込むことができます。その間、プロセスにはアドレス空間のどこにもそのポインターが含まれていない可能性があるため、ガベージコレクターはそのオブジェクトを収集し、ポインターを逆シリアル化すると...フープ。
-Bwmat

@Bwmat "Even"?そのようなファイルへのポインタを書くのはちょっと...とても手間がかかりそうです。とにかく、同じ深刻な問題がスタックオブジェクトへのポインタを悩ませています。コードの他の場所のファイルからポインタを読み戻すと、それらは消えてしまうかもしれません!無効なポインター値を逆シリアル化すること未定義の動作です。しないでください。
ハイド

もちろん、そのようなことをしている場合は注意する必要があります。これは、一般に、ガベージコレクターがc ++ですべての場合に(言語を変更せずに)「適切に」動作できないことを示すためのものでした
Bwmat

1
@ gnasher729:エム、いや?過去のエンドポインターはまったく問題ありませんか?
デデュプリケーター
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.