C ++にガベージコレクタがないのはなぜですか?


270

まず、ガベージコレクションのメリットがあるので、この質問はしていません。これを尋ねる私の主な理由は、Bjarne StroustrupがC ++にある時点でガベージコレクターが含まれると言ったことを知っていることです。

とはいえ、なぜ追加されていないのですか?C ++用のガベージコレクターはすでにいくつかあります。これは、「実行するより簡単に言う」タイプのものの1つにすぎませんか?それとも追加されていない(そしてC ++ 11では追加されない)理由は他にありますか?

相互リンク:

明確にするために、C ++が最初に作成されたときにガベージコレクターがなかった理由を理解しています。コレクターを追加できないのはなぜですか。


26
これは、嫌悪者が常に持ち出すC ++に関するトップ10の神話の1つです。ガベージコレクションは「組み込まれている」わけではありませんが、C ++を使用する簡単な方法がいくつかあります。他の人が私が下にできるよりも上手に答えたので、コメントを投稿します:)
davr

5
しかし、それが組み込みではないことの要点であり、自分で行う必要があります。高から低への実現性:組み込み、ライブラリ、自家製。私はC ++を自分で使用していますが、C ++は世界で最高の言語なので、間違いなく嫌いではありません。しかし、動的メモリ管理は面倒です。
QBziZ 2008

4
@Davr-私はC ++嫌いではありませんし、C ++にガベージコレクターが必要だと主張することすらしていません。Bjarne Stroustrupが追加されると述べており、それを実装しない理由が何であるかを知りたがっていたので、私は質問しています。
Jason Baker、

1
この記事は、Dobbs博士によるCおよびC ++用のBoehm Collectorで、CおよびC ++の両方で使用できるオープンソースのガベージコレクターについて説明しています。C ++デストラクタとC標準ライブラリでガベージコレクタを使用するときに発生する問題のいくつかについて説明します。
Richard Chambers

1
@rogerdpack:しかし、今のところそれほど有用ではありません(私の答えを参照してください...)ので、実装がそれに投資することはまずありません。
アインポクルム2017

回答:


160

暗黙的なガベージコレクションが追加された可能性がありますが、効果がありませんでした。おそらく、実装の複雑さだけでなく、人々が一般的なコンセンサスに十分早く到達できないことが原因です。

Bjarne Stroustrup自身からの引用:

オプションで有効にできるガベージコレクターがC ++ 0xの一部になることを期待していましたが、そのようなコレクターが他の言語と統合する方法の詳細な仕様だけで十分な技術的問題が発生しました(提供されている場合)。本質的にすべてのC ++ 0x機能の場合と同様に、実験的な実装が存在します。

ここでトピックについての良い議論があります

総括:

C ++は非常に強力であり、ほとんど何でも行うことができます。このため、パフォーマンスに影響を与える可能性のある多くのものを自動的にプッシュすることはありません。ガベージコレクションは、スマートポインター(参照カウントでポインターをラップするオブジェクトで、参照カウントが0に達すると自動的に削除されるオブジェクト)で簡単に実装できます。

C ++は、ガベージコレクションのない競合他社を念頭に置いて構築されました。効率性は、C ++がCや他のものと比較して批判を回避しなければならないという主要な懸念でした。

ガベージコレクションには2つのタイプがあります...

明示的なガベージコレクション:

C ++ 0xは、shared_ptrで作成されたポインターを介してガベージコレクションを実行します

必要な場合は使用できますが、必要ない場合は使用せずに済みます。

C ++ 0xを待ちたくない場合は、現在boost:shared_ptrも使用できます。

暗黙的なガベージコレクション:

ただし、透明なガベージコレクションはありません。ただし、これは将来のC ++仕様の焦点になります。

Tr1に暗黙のガベージコレクションがないのはなぜですか?

C ++ 0xのtr1が持っていなければならないことがたくさんあります、以前のインタビューでのBjarne Stroustrupは、tr1には彼が望んでいたほど多くはなかったと述べました。


71
私は考えになって C ++が私にガベージコレクションを強制場合は嫌い!なぜ人々は使用できないのsmart_ptr'sですか?ガベージコレクターを使用して、低レベルのUnixスタイルのフォークをどのように実行しますか?スレッド化などの他のものが影響を受けます。Python のガベージコレクションが原因で、Pythonにはグローバルインタープリターロックがあります(Cythonを参照)。C / C ++に入れないでください。ありがとうございます。
unixman83 2012

26
@ unixman83:参照カウントガベージコレクション(つまりstd::shared_ptr)の主な問題は循環参照であり、メモリリークが発生します。したがってstd::weak_ptr、サイクルを中断するために注意深く使用する必要があります。これは厄介です。マークアンドスイープスタイルのGCにはこの問題はありません。スレッド化/フォークとガベージコレクションの間に固有の非互換性はありません。JavaとC#はどちらも、高性能のプリエンプティブマルチスレッドと、ガベージコレクターを備えています。ほとんどのガベージコレクターは実行するために世界を停止しなければならないため、リアルタイムアプリケーションとガベージコレクターには関係する問題があります。
Andrew Tomazos 2013年

9
「参照カウントガーベジコレクション(つまり、主な問題はstd::shared_ptr、より良いパフォーマンスは通常、C ++を使用するための正当化であるため、皮肉でひどいパフォーマンス)循環参照である」... flyingfrogblog.blogspot.co.uk/2011/01/...
ジョンをハロップ

11
「低レベルのUnixスタイルのフォークをどのように実行しますか」。OCamlのようなGCが開発した言語が20年以上も同じようにしてきたのと同じです。
Jon Harrop 2013年

9
「Pythonのグローバルインタープリターロックは、ガベージコレクションが主な原因です。」ストローマンの議論。Javaと.NETの両方にGCがありますが、どちらにもグローバルロックはありません。
Jon Harrop 2013年

149

ここで議論に追加します。

ガベージコレクションには既知の問題があり、それらを理解することは、C ++には何もない理由を理解するのに役立ちます。

1.パフォーマンス?

最初の不満はしばしばパフォーマンスに関するものですが、ほとんどの人は自分が話していることを本当に理解していません。Martin Beckett問題が示すように、パフォーマンス自体ではなく、パフォーマンスの予測可能性である可能性があります。

現在、広く展開されているGCファミリは2つあります。

  • マークアンドスイープの種類
  • 参照カウントの種類

Mark And Sweep(全体的なパフォーマンス上の影響が少ない)高速ですが、それは「世界をフリーズ」症候群に苦しんでいる:すなわちGCはそのクリーンアップをしたまででGCのキックは、他のすべてが停止したとき。数ミリ秒で応答するサーバーを構築したい場合...一部のトランザクションは期待に応えられません:)

の問題Reference Countingは異なります。アトミックカウントが必要になるため、特にマルチスレッド環境では、参照カウントによりオーバーヘッドが追加されます。さらに、参照サイクルの問題があるため、これらのサイクルを検出して排除するための巧妙なアルゴリズムが必要です(一般的に、頻度は低くなりますが、「世界の凍結」によっても実装します)。一般に、今日の時点では、この種類(通常は応答性が高いか、むしろフリーズの頻度は少ないですが)はよりも低速ですMark And Sweep

「フリーズ・ザ・ワールド」の側面Reference CountingMark And Sweepない場合と同様のグローバルパフォーマンスを持つガベージコレクターを実装しようとしているエッフェルの実装者による論文を見ました。GC(通常)用に別のスレッドが必要でした。アルゴリズムは(最後に)少し恐ろしいものでしたが、このペーパーは、コンセプトを1つずつ紹介し、「単純な」バージョンから本格的なものへのアルゴリズムの進化を示すのに優れています。PDFファイルに手を戻すことができる場合にのみ読むことをお勧めします...

2.リソースの取得は初期化(RAII)です

C++リソースの所有権をオブジェクト内にラップして、リソースが適切に解放されるようにするという点で、これは一般的なイディオムです。ガベージコレクションがないため、主にメモリに使用されますが、他の多くの状況でも役立ちます。

  • ロック(マルチスレッド、ファイルハンドルなど)
  • 接続(データベース、別のサーバーなど)

アイデアは、オブジェクトの存続期間を適切に制御することです。

  • あなたがそれを必要とする限りそれは生きているべきです
  • 使い終わったら殺す必要があります

GCの問題は、GCが前者に役立ち、最終的に後で保証する場合、この "最終的な"では十分でない可能性があることです。ロックを解放する場合は、それが今すぐ解放されて、それ以上の呼び出しがブロックされないようにする必要があります。

GCを使用する言語には、2つの回避策があります。

  • スタックの割り当てが十分な場合はGCを使用しないでください。通常、これはパフォーマンスの問題のためですが、スコープではライフタイムが定義されているため、実際には役立ちます。
  • using構成...しかし、それは明示的な(弱い)RAIIですが、C ++ではRAIIは暗黙的であるため、ユーザーは(usingキーワードを省略して)意図せずにエラーを発生させることはできません。

3.スマートポインター

スマートポインタは、多くの場合、のメモリを処理するための特効薬として表示されC++ます。よく聞いたことがあります。スマートポインタがあるので、結局GCは必要ありません。

もっと間違ったことはありません。

スマートポインタが役立ちます。RAIIの概念auto_ptrunique_ptr使用すると、非常に便利です。とてもシンプルなので、自分で簡単に書くことができます。

所有権を共有する必要がある場合は、さらに難しくなります。複数のスレッド間で共有する可能性があり、カウントの処理にいくつかの微妙な問題があります。したがって、自然にに向かっていきshared_ptrます。

それは素晴らしいです、結局のところ、Boostはそのためですが、それは特効薬ではありません。実際の主な問題shared_ptrは、GCをエミュレートすることで実装されてReference Countingいますが、サイクル検出をすべて自分で実装する必要があります... Urg

もちろん、これは問題がありますがweak_ptr、残念ながらshared_ptr、これらのサイクルが原因で使用されているにもかかわらず、メモリリークはすでに発生しています。マルチスレッド環境では、検出が非常に困難です。

4.解決策は何ですか?

特効薬はありませんが、いつものように、それは確実に実現可能です。GCがない場合、所有権について明確にする必要があります。

  • 可能であれば、一度に1人の所有者がいることを好む
  • そうでない場合は、クラス図に所有権に関連するサイクルがないことを確認し、次の微妙なアプリケーションでそれらを壊してください weak_ptr

だから、確かに、GCがあれば素晴らしいでしょう...しかし、それはささいな問題ではありません。それまでの間は、袖をまくるだけです。


2
私は2つの答えを受け入れることができればいいのに!これは素晴らしいです。指摘すべきことの1つは、パフォーマンスに関して、別のスレッドで実行されるGCは実際にはかなり一般的です(Javaおよび.Netで使用されます)。確かに、それは組み込みシステムでは受け入れられないかもしれません。
Jason Baker

14
2種類だけ?どのようにコレクターをコピーするのですか?世代コレクター?さまざまな並行コレクター(ベイカーのハードリアルタイムトレッドミルを含む)?様々なハイブリッドコレクター?この分野の業界におけるまったくの無知は、私を時々驚かせます。
私の正しい意見だけ正しい

12
2種類しかないって言った?広く展開されているものは2つあると言いました。私の知る限り、Python、Java、C#はすべてMark and Sweepアルゴリズムを使用しています(Javaには参照カウントアルゴリズムがありました)。さらに正確に言うと、C#が世代別GCをマイナーサイクルに使用し、マークアンドスイープをメジャーサイクルに使用し、コピーを使用してメモリの断片化を解消しているようです。ただし、アルゴリズムの中心はMark And Sweepであると主張します。別のテクノロジーを使用する主流の言語を知っていますか?私はいつも学んで幸せです。
Matthieu M. 2010

3
あなたは、3つを使用する1つの主流言語に名前を付けました。
私の正しい意見だけ正しい

3
主な違いは、ジェネレーショナルGCとインクリメンタルGCが機能するために世界を止める必要がないことであり、GCポインター(因子新しいノードの数と、収集する必要性の基本的な予測によって決定できます)。コードのどこにノードの作成/変更が発生したかに関するデータを含めることで、GCをさらに進めることができます。これにより、予測を改善でき、Escape Analysisを無料で利用できます。
Keldon Alleyne、2012

56

どんなタイプ?組み込みの洗濯機コントローラー、携帯電話、ワークステーション、またはスーパーコンピューター向けに最適化する必要がありますか?
GUIの応答性またはサーバーの読み込みを優先する必要がありますか?
大量のメモリまたは大量のCPUを使用する必要がありますか?

C / c ++は、非常に多くの異なる状況で使用されています。ほとんどのユーザーにとって、ブーストスマートポインターのようなもので十分だと思います

編集-自動ガベージコレクターはパフォーマンスの問題ではありません(常にサーバーを追加購入できます)。これは予測できないパフォーマンスの問題です。
GCがいつキックするかわからないのは、ナルコレプティック航空会社のパイロットを採用するようなものです。


6
私は間違いなくあなたの主張を理解していますが、私は尋ねざるを得ませんでした。Javaは同じくらい多くのアプリケーションで使用されていないのですか?
Jason Baker、

35
いいえ。Javaは、C ++ほどのパフォーマンスが保証されていないという単純な理由で、高性能アプリケーションには適していません。したがって、携帯電話では見つかりますが、携帯電話のスイッチやスーパーコンピュータでは見つかりません。
Zathrus 2008

11
いつでもサーバーを追加購入できますが、顧客のポケットに既に入っている携帯電話用のCPUを常に追加購入できるとは限りません。
Crashworks、2010年

8
Javaは、CPU効率において多くのパフォーマンスキャッチアップを行いました。本当に扱いにくい問題はメモリ使用量であり、Javaは本質的にC ++よりもメモリ効率が低いです。そして、その非効率性は、ガベージコレクションされているという事実によるものです。ガベージコレクションは高速でメモリ効率の高いものにすることはできません。これは、GCアルゴリズムの動作速度を調べると明らかになります。
Nate CK、

2
@Zathrus Javaは、レイテンシ(リアルタイムのブー)ではなく、メモリフットプリントではなく、最適化jitのスループットb / cで勝つことができます。
2011年

34

C ++にガベージコレクションが組み込まれていない最大の理由の1つは、ガベージコレクションをデストラクタでうまく機能させることが本当に難しいことです。私の知る限り、完全に解決する方法はまだ誰も知りません。対処すべき問題がたくさんあります:

  • オブジェクトの確定的ライフタイム(参照カウントではこれが得られますが、GCでは得られません。それほど大きな問題ではないかもしれません)。
  • オブジェクトがガベージコレクションされているときにデストラクタがスローするとどうなりますか?ほとんどの言語はこの例外を無視します。それを転送できるcatchブロックがないためですが、これはおそらくC ++の許容できる解決策ではありません。
  • それを有効/無効にする方法は?当然、それはおそらくコンパイル時の決定ですが、GC用に作成されたコードとNOT GC用に作成されたコードは非常に異なり、おそらく互換性がありません。これをどのように調整しますか?

これらは直面している問題のほんの一部です。


17
GCとデストラクタは、Bjarneからの素晴らしい回避策によって、解決された問題です。それがGCのポイントではないので、デストラクタはGC中に実行されません。C ++のGCは、無限の他のリソースではなく、無限メモリの概念を作成するために存在します。
MSalters 2008

2
デストラクタが実行しないと、言語のセマンティクスが完全に変わります。このオブジェクトを明示的にGCできるようにするには、少なくとも新しいキーワード "gcnew"か何かが必要になると思います(したがって、メモリ以外のリソースをラップするためにこのオブジェクトを使用しないでください)。
グレッグロジャース

7
これは偽の議論です。C ++には明示的なメモリ管理があるため、すべてのオブジェクトをいつ解放する必要があるかを把握する必要があります。GCを使用すると、それはさらに悪くはありません。むしろ、問題は、特定のオブジェクト、つまり削除時に特別な考慮が必要なオブジェクトが解放されたときに把握することで軽減されます。JavaおよびC#でのプログラミングの経験から、オブジェクトの大部分は特別な考慮を必要とせず、GCに安全に任せることができることがわかります。結局のところ、C ++のデストラクタの主な機能の1つは、GCが自動的に処理する子オブジェクトを解放することです。
Nate CK

2
@ NateC-K:GCと非GC(おそらく最大のもの)で改善された点の1つは、参照が存在する限り、すべての参照が同じオブジェクトを指し続けることを保証する固体GCシステムの能力です。Disposeオブジェクトを呼び出すと、オブジェクトが使用できなくなる可能性がありますが、オブジェクトが生存していたときにそのオブジェクトを指し示していた参照は、オブジェクトが死んだ後も引き続きそれを行います。対照的に、GC以外のシステムでは、参照が存在しているときにオブジェクトを削除できます。これらの参照のいずれかが使用された場合に発生する可能性のある大混乱にはほとんど制限がありません。
スーパーキャット2013年

22

これは古い質問ですが、誰も対処していないという問題がまだ1つあります。ガベージコレクションを指定することはほとんど不可能です。

特に、C ++標準は、実装がその動作を実現する方法ではなく、外部から観察可能な動作の観点から言語を指定するように細心の注意を払っています。ただし、ガベージコレクションの場合、事実上、外部から観察可能な動作はありません。

ガベージコレクションの一般的な考え方は、メモリ割り当てが成功することを保証するための合理的な試みを行う必要があるということです。残念ながら、ガベージコレクターが動作していても、メモリ割り当てが成功することを保証することは基本的に不可能です。これはある程度は当てはまりますが、C ++の場合は特にそうです。コレクションサイクル中にメモリ内のオブジェクトを移動するコピーコレクタ(または類似のもの)を(おそらく)使用することができないためです。

オブジェクトを移動できない場合、割り当てを行うための単一の連続したメモリスペースを作成できません。つまり、ヒープ(またはフリーストア、または呼び出したいもの)ができることを意味し、おそらく、時間の経過とともに断片化します。これにより、要求された量よりも多くのメモリが解放されている場合でも、割り当てが成功しないことがあります。

正確に同じパターンの割り当てを繰り返し繰り返し、それが初めて成功した場合、割り当てられたメモリが提供されていれば、後続の反復で引き続き成功することを(本質的に)保証するいくつかの保証が考えられるかもしれませんがイテレーション間でアクセスできなくなりました。それは本質的に役に立たないというとても弱い保証ですが、私はそれを強化するための合理的な希望を見ることができません。

それでも、C ++で提案されているものよりも強力です。の先の提案 [警告:PDF](つまりGOTがドロップされた)は、まったく何も保証するものではありませんでした。提案の28ページで、外部から観察可能な動作の邪魔になったのは、次のような単一の(非規範的)注釈です。

[注:ガベージコレクションされたプログラムの場合、高品質のホストされた実装は、再利用する到達不能メモリの量を最大化しようとする必要があります。—エンドノート]

少なくとも私にとって、これは 投資収益率について深刻な問題をます。既存のコードを破壊し(正確にはどれくらいかはわかりませんが、間違いなくかなりの数です)、実装に新しい要件を課し、コードに新しい制限を課します。その結果、何もないかもしれません。

せいぜい、Javaでのテストに基づいたプログラムが得られますでの現在の速度と同じ速度で実行するには、おそらく約6倍のメモリが必要になる。さらに悪いことに、ガベージコレクションは最初からJavaの一部でした-C ++はガベージコレクターに十分な制限を課すため、コストと利益の比率はさらに悪くなります(たとえ提案された保証を超えて、あると仮定したとしても)いくつかの利点)。

状況を数学的に要約します。これは複雑な状況です。数学者なら誰でも知っているように、複素数には実数と虚数の2つの部分があります。ここにあるのは現実のコストですが、(少なくともほとんどの場合)想像上のメリットです。


適切な操作のためにすべてのオブジェクトを削除する必要があると指定し、削除されたオブジェクトのみが収集の対象になると指定した場合でも、そのような言語が確実にすることができるので、参照追跡ガベージコレクションのコンパイラーサポート依然として役立つ可能性があります。削除されたポインター(参照)の使用は、未定義の動作を引き起こすのではなく、トラップすることが保証されます。
スーパーキャット2013

2
Javaの場合でも、GCは実際に有用なAFAIKを実行するように指定されていません。それはfreeあなたを必要とするかもしれません(私freeがC言語に類似していることを意味します)。しかし、Javaはファイナライザなどの呼び出しを保証しません。実際、C ++は、コミットデータベースの書き込み、ファイルハンドルのフラッシュなどを実行するために、Javaよりもはるかに多くのことを行います。Javaは「GC」を持っていると主張しますが、Java開発者はclose()常に細心の注意を払って呼び出す必要があり、リソース管理について十分に認識し、close()遅すぎたり遅すぎたりしないように注意する必要があります。C ++はそれから私たちを解放します。...(続き)
アーロン・マクダイド2014年

2
..少し前の私のコメントは、Javaを批判するためのものではありません。私は「ガベージコレクション」という用語が非常に奇妙な用語であることを観察しているだけです。それは、人々が考えているよりもはるかに少ないことを意味します。そのため、それが何を意味するのかを明確にせずに議論することは困難です。
アーロン・マクダイド2014年

@AaronMcDaid GCが非メモリリソースをまったく支援しないのは事実です。幸いなことに、そのようなリソースは、メモリと比較するとほとんど割り当てられません。さらに、それらを割り当てたメソッドでそれらの90%以上を解放できるため、それをtry (Whatever w=...) {...}解決します(そして、忘れると警告が表示されます)。残りのものもRAIIに問題があります。close()「常時」の呼び出しは、数万行に1回程度であることを意味するので、それほど悪くはありませんが、メモリはほぼすべてのJava行に割り当てられます。
maaartinus 2017

15

自動ガベージコレクションが必要な場合は、C ++用の商用およびパブリックドメインの適切なガベージコレクタがあります。ガベージコレクションが適切なアプリケーションの場合、C ++は、他のガベージコレクション言語に匹敵するパフォーマンスを持つ優れたガベージコレクション言語です。C ++での自動ガベージコレクションについては、C ++プログラミング言語(第4版)を参照してください。Hans-Jも参照してください。CおよびC ++ガベージコレクションの Boehmのサイトアーカイブ)。

また、C ++は、ガベージコレクタがなくてもメモリ管理を安全かつ暗黙的に行えるプログラミング手法をサポートしています。。ガベージコレクションは最後の選択肢であり、リソース管理の不完全な処理方法だと思います。それはそれが決して役に立たないことを意味するのではなく、多くの状況でより良いアプローチがあるということだけです。

出典:http : //www.stroustrup.com/bs_faq.html#garbage-collection

なぜそれが組み込まれていないのかについては、私が正しく覚えていれば、GCが登場する前に発明されたものであり、いくつかの理由で言語にGCがあったとは思えません(IEのCとの下位互換性)

お役に立てれば。


「他のガベージコレクションされた言語に匹敵するパフォーマンスを持つ」。引用?
Jon Harrop 2013年

1
リンクが切れました。この回答は5年前に書きました。
レイン

1
わかりました、私はこれらの主張のいくつかの独立した検証を望んでいました、すなわち、StroustrupまたはBoehmによるものではありません。:-)
Jon Harrop 2013年

12

Stroustrupは2013 Going Nativeカンファレンスでこれについていくつかの良いコメントをしました。

この動画では、約25分50秒にスキップしてください。(私は実際にビデオ全体を見ることをお勧めしますが、これはガベージコレクションに関するものにスキップします。)

オブジェクトと値を直接(および明示的に)使用することを避けて(明示的に)使用することを避け、オブジェクト(および値)を直接処理することを簡単(かつ安全、予測可能、読みやすく、簡単に教える)に優れた言語がある場合ヒープ、そしてあなたもしませんしたいガベージコレクションを。

最新のC ++、およびC ++ 11にあるものでは、限られた状況を除いて、ガベージコレクションは望ましくありません。実際、優れたガベージコレクターが主要なC ++コンパイラーの1つに組み込まれていても、あまり使用されないと思います。それは次のようになります簡単に GCを避けるために、ではない難しく、。

彼はこの例を示しています:

void f(int n, int x) {
    Gadget *p = new Gadget{n};
    if(x<100) throw SomeException{};
    if(x<200) return;
    delete p;
}

これはC ++では安全ではありません。しかし、Javaでも安全ではありません。C ++では、関数が早く戻ると、deleteが呼び出されることはありません。しかし、Javaなどの完全なガベージコレクションがある場合、オブジェクトが「将来のある時点で」破棄されるという提案が表示されるだけです(更新:これはさらに悪いことです。Javaはファイナライザを呼び出すことを約束します-決して呼び出されることはありません)。ガジェットが開いたファイルハンドル、データベースへの接続、または後でデータベースへの書き込みのためにバッファリングしたデータを保持している場合、これは十分ではありません。これらのリソースをできるだけ早く解放するために、ガジェットが終了したらすぐに破棄する必要があります。データベースサーバーが、不要になった何千ものデータベース接続に悩まされることを望まない-プログラムが機能していることを知らない。

それで、解決策は何ですか?いくつかのアプローチがあります。オブジェクトの大部分に使用する明白なアプローチは次のとおりです。

void f(int n, int x) {
    Gadget p = {n};  // Just leave it on the stack (where it belongs!)
    if(x<100) throw SomeException{};
    if(x<200) return;
}

これにより、入力する文字が少なくなります。new邪魔になりません。Gadget2回入力する必要はありません。オブジェクトは関数の最後で破棄されます。これがあなたが望むものであれば、これは非常に直感的です。 Gadgetsが同じに振る舞うintかをdouble。予測可能で、読みやすく、教えやすい。すべてが「価値」です。大きな値の場合もありますが、ポインタ(または参照)で得られるこの「遠くでのアクション」がないため、値を教える方が簡単です。

作成するオブジェクトのほとんどは、それらを作成した関数でのみ使用され、おそらく子関数への入力として渡されます。プログラマは、オブジェクトを返すときや、ソフトウェアの広く分離された部分でオブジェクトを共有するときに、「メモリ管理」について考える必要はありません。

範囲と寿命は重要です。ほとんどの場合、ライフタイムがスコープと同じであるほうが簡単です。理解しやすく、教えるのも簡単です。別の有効期間が必要な場合、shared_ptrたとえば、これを使用して、これを実行しているコードを読み取ることは明らかです。(または(大きな)オブジェクトを値で返し、移動セマンティクスまたはを活用しますunique_ptr

これは効率の問題のように見えるかもしれません。ガジェットを返却したい場合はどうなりfoo()ますか?C ++ 11の移動セマンティクスにより、大きなオブジェクトを簡単に返すことができます。書くGadget foo() { ... }だけで動作し、すばやく動作します。&&自分でいじる必要はありません。値によって物事を返すだけで、言語は必要な最適化を行うことができます。(C ++ 03より前でも、コンパイラーは不要なコピーを回避するために非常に優れた機能を果たしました。)

Stroustrupがビデオの他の場所で述べたように(言い換え):「オブジェクトをコピーしてから元のオブジェクトを破壊することを主張するのはコンピューターサイエンティストだけです(聴衆は笑います。)オブジェクトを新しい場所に直接移動しないのはなぜですか? (コンピューター科学者ではなく)期待しています。」

オブジェクトのコピーが1つだけ必要であることを保証できる場合は、オブジェクトの存続期間を理解する方がはるかに簡単です。必要なライフタイムポリシーを選択でき、必要に応じてガベージコレクションも利用できます。しかし、他のアプローチの利点を理解すると、ガベージコレクションが設定のリストの一番下にあることがわかります。

それでもうまくいかない場合は、を使用するunique_ptrか、失敗してくださいshared_ptr。よく書かれたC ++ 11は、メモリ管理に関して、他の多くの言語よりも短く、読みやすく、教えやすいです。


1
GCは、リソースを取得しないオブジェクト(つまり、「通知があるまで」に代わって他のエンティティに何かを依頼する)にのみ使用する必要があります。場合はGadgetその代理で何かをする何かを聞いていません(Javaへの)意味のない場合は、元のコードは、Javaで完全に安全だろうdelete文は削除されました。
スーパーキャット2015

@supercat、退屈なデストラクタを持つオブジェクトは興味深いです。(私は「ボーリング」を定義していませんが、基本的に、メモリの解放を除いて、呼び出す必要のないデストラクタです)。が「つまらない」shared_ptr<T>場合、個々のコンパイラが特別に処理する可能性がありますT。そのタイプの参照カウンターを実際に管理せず、代わりにGCを使用することを決定できます。これにより、開発者が気付かなくてもGCを使用できます。A shared_ptrは、適切なの場合、単にGCポインターと見なすことができますT。しかし、これには制限があり、多くのプログラムが遅くなります。
アーロン・マクデイド、2015

一部の使用パターンは一方では非常にうまく機能し、もう一方では非常に不十分であるため、優れた型システムでは、GCとRAIIで管理されるヒープオブジェクトに異なる型が必要です。.NETまたはJavaでは、文string1=string2;は文字列の長さに関係なく非常に高速に実行され(文字通り、レジスタロードとレジスタストアにすぎません)、上記の文string2が書き込まstring1れると、古い値または新しい値のいずれかが保持され、未定義の動作はありません)。
スーパーキャット2015

C ++では、aの割り当てにはshared_ptr<String>多くの背後での同期が必要でありString、変数の読み取りと書き込みが同時に行われる場合、aの割り当ては奇妙な動作をする場合があります。String同時に書き込みと読み取りを行いたいケースはそれほど一般的ではありませんが、たとえば一部のコードが進行中のステータスレポートを他のスレッドで利用できるようにしたい場合に発生する可能性があります。.NETとJavaでは、このようなことは「機能する」だけです。
スーパーキャット2015

1
@curiousguy何も変更されていません。適切な予防策を講じない限り、Javaはコンストラクタが終了するとすぐにファイナライザを呼び出すことができます。ここで実際の例:「finalize()はJava 8で強力に到達可能なオブジェクトで呼び出されました」。結論は、この機能を決して使用しないことであり、ほとんどすべての人が言語の歴史的な設計ミスであることに同意します。そのアドバイスに従うと、言語は私たちが大好きな決定論を提供します。
Holger

11

最新のC ++はガベージコレクションを必要としないからです。

この問題に関するBjarne StroustrupのFAQ 回答は次のように述べています

ゴミは嫌いです。ポイ捨ては嫌いです。私の理想は、ガベージを生成しないことによってガベージコレクターの必要性を排除することです。今ではそれが可能です。


最近書かれたコード(C ++ 17および公式のコアガイドラインに従って)の状況 は次のとおりです。

  • ほとんどのメモリ所有権関連のコードはライブラリ(特にコンテナを提供するもの)にあります。
  • メモリの所有権を含むコードのほとんどの使用はRAII パターンに従っているため、構築時に割り当てが行われ、破棄時に割り当てが解除されます。これは、何かが割り当てられたスコープを終了するときに発生します。
  • 明示的にメモリを直接割り当てたり割り当て解除したりすることはありません
  • 生のポインタはメモリを所有していないため(ガイドラインに従っている場合)、それらを渡すことによってリークすることはできません。
  • メモリ内の値のシーケンスの開始アドレスをどのように渡すのか疑問に思っている場合は、スパンを使用してそれを行います。生のポインタは必要ありません。
  • 所有する「ポインター」が本当に必要な場合は、C ++の標準ライブラリスマートポインターを使用します。 これらのポインターはリークすることがなく、かなり効率的です(ただし、ABIはそれを妨げることがあります)。または、「所有者ポインタ」を使用して、スコープの境界を越えて所有権を渡すことができます。これらは一般的ではなく、明示的に使用する必要があります。しかし採用されたとき-それらはリークに対する素晴らしい静的チェックを可能にします。

「そうだね。でもどうだ...

...昔のC ++の記述方法でコードを記述した場合はどうなりますか?」

確かに、あなたは可能性があり、いつもと同じ、それはコンパイルして実行(リーク)します-ちょうどガイドラインのすべてを無視して、漏洩アプリケーション・コードを記述します。

しかし、それは「そうしないでください」という状況ではなく、開発者は高潔であり、多くの自己制御を行使することが期待されています。非準拠のコードを記述するのは簡単ではありません。また、記述するのが速くなく、パフォーマンスも優れています。また、準拠するコードが提供し、期待するものとの「インピーダンスの不一致」が増加するので、次第に記述が難しくなります。

...私ならreintrepret_cast?または複雑なポインター演算をしますか?または他のそのようなハック?」

実際、気を付ければ、ガイドラインを順守しているにも関わらず、物事を台無しにするコードを書くことができます。だが:

  1. これを行うことはまれです(コード内の場所に関して、必ずしも実行時間の割合に関してではない)。
  2. これは、誤ってではなく、意図的に行うだけです。
  3. そうすることで、ガイドラインに準拠したコードベースで目立つようになります。
  4. とにかく、別の言語でGCをバイパスするようなコードです。

...ライブラリ開発?」

C ++ライブラリの開発者は、生のポインターを含む安全でないコードを作成し、責任を持って慎重にコーディングする必要があります。ただし、これらは、エキスパートが作成した自己完結型のコードです(さらに重要なことに、エキスパートがレビューします)。


だから、それはBjarneが言ったのと同じです。一般に、ゴミを収集する動機はまったくありません。GCはC ++では問題になりません。

カスタムの割り当てと割り当て解除の戦略を採用したい場合、特定の特定のアプリケーションではGCは興味深い問題ではありません。それらについては、言語レベルのGCではなく、カスタムの割り当てと割り当て解除が必要です。


まあ、それはあなたが文字列をグラインディングしている場合は(GCが必要です)..あなたが断片的に構築していて、異なる長さに処理して再構築し、未使用のものを削除し、他のものを組み合わせるなど、大きな文字列配列があると想像してください。私が対処するために高級言語に切り替えなければならなかったので知っています。(もちろん、独自のGCを構築することもできます)。
www-0av-Com

2
@ user1863152:カスタムアロケータが役立つケースです。それでも言語統合GCは必要ありません...
einpoklum

アインポクルムへ:真実。それはコースのための単なる馬です。私の要件は、動的に変化するガロンの輸送旅客情報を処理することでした。魅力的な主題..本当にソフトウェア哲学に帰着します。
www-0av-Com 2018

Javaと.NETの世界が最終的に発見したGCには、大きな問題があります-それはスケーリングしません。ささいなソフトウェアを使用して最近行っているように、メモリ内に数十億のライブオブジェクトがある場合、GCから何かを隠すためのコードを書き始める必要があります。Javaと.NETでGCを使用するのは負担です。
Zach Saw

10

C ++の背後にある考え方は、使用しない機能に対してパフォーマンスへの影響を一切払わないというものでした。したがって、ガベージコレクションを追加すると、一部のプログラムはCと同じようにハードウェア上で直接実行され、一部のプログラムはある種のランタイム仮想マシン内で実行されます。

サードパーティのガベージコレクションメカニズムにバインドされているスマートポインターの形式を使用することを妨げるものはありません。MicrosoftがCOMでそのようなことを行っていて、うまくいきませんでした。


2
GCにはVMが必要だとは思いません。コンパイラーは、グローバル状態を更新するためにすべてのポインター操作にコードを追加できますが、バックグラウンドで別のスレッドが実行され、必要に応じてオブジェクトが削除されます。
user83255 2009年

3
同意する。仮想マシンは必要ありませんが、何かバックグラウンドでメモリを管理するようになったら、実際の「電線」を残してVMのような状況になったように思います。
ウリ


4

元のC言語の背後にある基本的な原則の1つは、メモリは一連のバイトで構成され、コードはそれらのバイトが使用されている正確な瞬間にそれらのバイトが何を意味するかを考慮するだけです。最新のCでは、コンパイラーは追加の制限を課すことができますが、Cには、ポインターをバイトのシーケンスに分解し、同じ値を含むバイトのシーケンスを組み立ててポインターにして、そのポインターを以前のオブジェクトにアクセスします。

この機能は、一部の種類のアプリケーションでは有用である場合があり、必須である場合もありますが、その機能を含む言語は、あらゆる種類の有用で信頼できるガベージコレクションをサポートする機能が非常に制限されます。コンパイラーは、ポインターを構成するビットで行われたすべてのことを知らない場合、ポインターを再構築するのに十分な情報がユニバースのどこかに存在するかどうかを知る方法がありません。その情報は、コンピュータがそれを知っていてもアクセスできない方法で格納される可能性があるため(たとえば、ポインタを構成するバイトが、誰かが書き込むのに十分長い間画面に表示された可能性があります)一枚の紙にそれらを書き留めます)、ポインタが将来使用される可能性があるかどうかをコンピュータが知ることは文字通り不可能かもしれません。

多くのガベージコレクションされたフレームワークの興味深い奇妙な点は、オブジェクト参照が、そこに含まれるビットパターンではなく、オブジェクト参照に保持されているビットと他の場所に保持されている他の情報との関係によって定義されていることです。CおよびC ++では、ポインターに格納されたビットパターンがオブジェクトを識別する場合、オブジェクトが明示的に破棄されるまで、そのビットパターンはそのオブジェクトを識別します。典型的なGCシステムでは、オブジェクトはある時点でビットパターン0x1234ABCDで表される場合がありますが、次のGCサイクルで0x1234ABCDへのすべての参照が0x4321BABEへの参照に置き換えられ、オブジェクトは後者のパターンで表されます。オブジェクト参照に関連付けられたビットパターンを表示し、後でそれをキーボードから読み取る場合でも、


これは本当に良い点です。最近、ポインタからいくつかのビットを盗みました。そうしないと、キャッシュミスの量が非常に多くなるからです。
2017年

@PasserBy:64ビットポインターを使用する多くのアプリケーションが、スケーリングされた32ビットポインターをオブジェクト参照として使用するか、ほとんどすべてを4GiBのアドレス空間に保持し、特別なオブジェクトを使用して高位からデータを格納/取得することで、より多くの利益を得ると思いますを超えて高速ストレージ?マシンには十分なRAMが搭載されており、32ビットポインターの2倍のキャッシュを使用する場合を除いて、64ビットポインターのRAM使用量は問題にならない場合があります。
スーパーキャット2017年

3

技術的な話はすべて、コンセプトを複雑にしています。

すべてのメモリのGCをC ++に自動的に配置する場合は、Webブラウザのようなものを検討してください。Webブラウザーは、完全なWeb文書をロードし、Webスクリプトを実行する必要があります。ドキュメントツリーにWebスクリプト変数を格納できます。多数のタブが開いているブラウザのBIGドキュメントでは、GCが完全なコレクションを実行するたびに、すべてのドキュメント要素もスキャンする必要があります。

ほとんどのコンピュータでは、これはページエラーが発生することを意味します。したがって、主な理由は、質問に答えるために、ページエラーが発生することです。これは、PCが大量のディスクアクセスを開始するときにわかります。これは、無効なポインタを証明するために、GCが大量のメモリにアクセスする必要があるためです。大量のメモリを使用する本物のアプリケーションがある場合、すべてのオブジェクトをスキャンする必要があるすべてのコレクションは、ページ障害のために大混乱で​​す。ページフォールトは、仮想メモリがディスクからRAMに読み戻す必要がある場合です。

したがって、正しい解決策は、アプリケーションをGCを必要とする部分と必要としない部分に分割することです。上記のWebブラウザーの例の場合、ドキュメントツリーがmallocで割り当てられたものの、JavaScriptがGCで実行された場合、GCが実行されるたびに、メモリのごく一部とメモリのすべてのPAGED OUT要素のみがスキャンされます。ドキュメントツリーはページを戻す必要はありません。

この問題をさらに理解するには、仮想メモリとそれがコンピュータにどのように実装されているかを調べます。RAMがそれほど多くない場合でも、プログラムで2GBを使用できるということです。32BItシステム用に2GBのRAMを備えた最近のコンピューターでは、1つのプログラムのみが実行されていれば、このような問題はありません。

追加の例として、すべてのオブジェクトをトレースする必要がある完全なコレクションを考えます。最初に、ルート経由で到達可能なすべてのオブジェクトをスキャンする必要があります。次に、手順1で表示されたすべてのオブジェクトをスキャンします。次に、待機中のデストラクタをスキャンします。次に、再びすべてのページに移動し、すべての非表示オブジェクトをオフにします。これは、多くのページが複数回スワップアウトされて戻される可能性があることを意味します。

つまり、短くするための私の答えは、すべてのメモリに触れた結果として発生するページ障害の数が原因で、プログラム内のすべてのオブジェクトの完全なGCが実行不可能になるため、プログラマはGCをスクリプトのようなものの補助として見なさなければならないということです。とデータベースは動作しますが、手動のメモリ管理で通常のことを行います。

もちろん、他の非常に重要な理由はグローバル変数です。コレクターがグローバル変数ポインターがGCにあることを知るには、特定のキーワードが必要になるため、既存のC ++コードは機能しません。


3

短い回答:ガベージコレクションを効率的に(マイナーな時間とスペースのオーバーヘッドで)正しく、いつでも(可能なすべてのケースで)行う方法はわかりません。

長い答え:Cと同様に、C ++はシステム言語です。つまり、オペレーティングシステムなどのシステムコードを記述しているときに使用されます。つまり、C ++はCと同様に、可能な限り最高のパフォーマンスをメインターゲットとして設計されています。言語の標準は、パフォーマンス目標を妨げる可能性のある機能を追加しません。

これは質問を一時停止します。なぜガベージコレクションがパフォーマンスを妨げるのですか?主な理由は、実装に関しては、すべての場合において、私たち[コンピューター科学者]が最小限のオーバーヘッドでガベージコレクションを行う方法を知らないためです。したがって、C ++コンパイラとランタイムシステムがガベージコレクションを常に効率的に実行することは不可能です。一方、C ++プログラマーは自分の設計/実装を知っている必要があり、ガベージコレクションを最適に行う方法を決定するのに最適な人物です。

最後に、制御(ハードウェア、詳細など)とパフォーマンス(時間、スペース、電力など)が主な制約ではない場合、C ++は書き込みツールではありません。他の言語の方が適切に機能し、必要なオーバーヘッドを伴う、より多くの[非表示]ランタイム管理を提供できます。


3

C ++とJavaを比較すると、Javaがそうであったのに対し、C ++は暗黙的なガベージコレクションを考慮して設計されていないことがわかります。

C-Styleに任意のポインターのようなものがあると、GC実装にとって悪いだけでなく、大量のC ++-legacy-codeの下位互換性も破壊されます。

さらに、C ++は、複雑なランタイム環境ではなく、スタンドアロンの実行可能ファイルとして実行することを目的とした言語です。

全体として:はい、ガベージコレクションをC ++に追加することは可能かもしれませんが、継続性のために、そうしない方が良いでしょう。


1
メモリの解放とデストラクタの実行は、まったく別の問題です。(JavaにはPITAであるデストラクタがありません。)GCはメモリを解放し、dtorを実行しません。
curiousguy

0

主に2つの理由があります。

  1. 必要ないから(IMHO)
  2. C ++の要であるRAIIとはほとんど互換性がないため

C ++はすでに手動のメモリ管理、スタック割り当て、RAII、コンテナ、自動ポインタ、スマートポインタを提供しています...それで十分でしょう。ガベージコレクターは、だれがどのオブジェクトを所有する必要があるか、いつリソースを解放する必要があるかについて5分間費やしたくない怠惰なプログラマー向けです。これは、C ++で行う方法ではありません。


ガベージコレクションなしに実装するのが本質的に難しい(新しい)アルゴリズムは数多くあります。時間は進んだ。イノベーションは、(ガベージコレクションの)高級言語によく一致する新しい洞察からも生まれます。これらのいずれかをGCフリーC ++にバックポートしてみてください。道路の段差に気づくでしょう。(私は例を挙げるべきだと知っていますが、私はちょっと急いでいます。申し訳ありません。現在、考えられるのは、参照カウントが機能しない永続データ構造を中心に展開しています。)
BitTickler 2018

0

ガベージコレクションを課すことは、実際には低レベルから高レベルのパラダイムシフトです。

文字列がガベージコレクションを使用する言語で処理される方法を見ると、それらは高レベルの文字列操作関数のみを許可し、文字列へのバイナリアクセスを許可していないことがわかります。簡単に言えば、バイトを描画するだけの場合でも、すべての文字列関数は最初にポインタをチェックして文字列の場所を確認します。したがって、ガベージコレクションを使用する言語で文字列の各バイトを処理するループを実行している場合、文字列がいつ移動したかを認識できないため、ループごとに基本位置とオフセットを計算する必要があります。次に、ヒープ、スタック、スレッドなどについて考える必要があります。

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