2008-08-22 8 views
46

Pythonは参照カウントメソッドを使用してオブジェクトの存続時間を処理します。だから、もう使えないオブジェクトはすぐに破棄されます。なぜJavaとPythonのガベージコレクションの方法が異なるのですか?

しかし、Javaでは、GC(ガベージコレクタ)は特定の時刻に使用されなくなったオブジェクトを破棄します。

Javaがこの戦略を選択する理由とそのメリットは何ですか?

これはPythonのアプローチより優れていますか?

答えて

43

参照カウントを使用することには欠点があります。 Aが参照BとBがCとCの参照Bを参照しているとします。AがBへの参照を削除すると、BとCの両方が参照カウント1を持ち、削除されません従来の参照カウントを使用します。 CPython(参照カウントはPython自体の一部ではなく、そのC実装の一部です)は周期的に実行される個別のガベージコレクションルーチンで循環参照をキャッチします...

もう1つの欠点:参照カウントは実行を遅くする可能性があります。オブジェクトが参照され、逆参照されるたびに、インタプリタ/ VMは、カウントが0になったかどうかをチェックして、それがあれば割り当てを解除する必要があります。ガベージコレクションはこれを行う必要はありません。

また、別のスレッドでガベージコレクションを実行することもできます(少し難しいかもしれませんが)。 RAMがたくさんあるマシンでは、メモリをゆっくり使用するプロセスでは、GCをやりたいとは思わないかもしれません!参照カウントは、パフォーマンスの面で少し欠点になります...

+16

注目に値する追加の違いは、Javaの怠惰なアプローチをするまで、JVMは、一時的に実際に必要なよりもはるかに多くのメモリを使用する可能性があり、一方、参照カウントを経由して熱心GCは常に、(循環依存の場合を除く)「最小」のメモリを使用することですGCを実行すると、元の状態に戻ります。 Javaのアプローチはメモリを犠牲にして速度を上げ、メモリが豊富な場合に利点があります。それが不十分なときは、Pythonのアプローチがうまくいくでしょう。 –

+1

参照カウントは、マーク/スイープGCよりもいくつかの理由で低速です。1.参照カウントを更新するためのメモリ書き込みは高価で、同期が必要なために並行性の問題が発生します。 2.参照カウント自体が余分なメモリを使用するため、オブジェクトのサイズが大きくなり、キャッシュの負担が大きくなります。 – mikera

13

ダレントーマスは良い答えを与えます。しかし、JavaとPythonのアプローチの大きな違いの1つは、一般的な場合(循環参照なし)の参照カウントでは、オブジェクトが不確定な後日よりもすぐにクリーンアップされるということです。

例えば、私は、このような

def parse_some_attrs(fname): 
    return open(fname).read().split("~~~")[2:4] 

としてはCPythonでずさんな、非ポータブルなコードを書くことができますし、私は開いているファイルのファイル記述子があるため、すぐにオープンを基準として、すぐにクリーンアップされますファイルがなくなると、ファイルはガベージコレクトされ、ファイルディスクリプタは解放されます。もちろん、JythonやIronPython、おそらくはPyPyを実行すると、あとでガベージコレクタが実行されることはありません。おそらく私はファイル記述子を使い果たし、私のプログラムはクラッシュするでしょう。

ですから、

def parse_some_attrs(fname): 
    with open(fname) as f: 
     return f.read().split("~~~")[2:4] 

のように見えますが、時には、人々はそれは時々あなたのコードを少し短くすることができるので、常に自分のリソースを解放するために、参照カウントに頼るのが好き。コードを書くべきである(SHOULD)

最高のガベージコレクタは最高のパフォーマンスを備えたもので、現在はJavaスタイルの世代別ガベージコレクタで、別のスレッドで実行でき、これらのクレイジーな最適化などがあります。コードを書く方法との違いは無視して、理想的には存在しないはずです。

+4

良い答えです。私にとっては、2番目の例で明示的な 'close()'を使用していないという答えから1つの重要な点があります。 Pythonで記述するコードが非常に少なくなっています。これはhttp://docs.python.org/howto/doanddont.htmlで説明されています(「オープンで検索」) –

2

最新のSun Java VMには、実際に調整が可能な複数のGCアルゴリズムがあります。Java VMの仕様では、実際のGCの動作を指定することを意図的に省略して、異なるVMに対して異なる(複数の)GCアルゴリズムを可能にしています。

たとえば、デフォルトのSun Java VM GC動作の「世界を止める」アプローチを嫌うすべての人々にとって、IBM's WebSphere Real TimeのようなリアルタイムのアプリケーションをJavaで実行できるVMがあります。

Java VMの仕様は一般に公開されているため、CPythonのGCアルゴリズムを使用するJava VMの実装を誰も止めることは理論的にはありません。

+0

おそらく単純な参照カウントを禁止しています(参照カウントに何かを状況)。私はPythonがこれをどのように処理するのかよくわかりませんが、サイクルのチェックには少なくとも何らかのチェックがあると思います。 –

2

参照カウントは、マルチスレッド環境で効率的に行うのが特に困難です。ハードウェア支援トランザクションやそれに類する(現在)珍しいアトミック命令に陥ることなく、どうやってそれをやり始めようとしているのか分かりません。

参照カウントは簡単に実装できます。 JVMは、競合する実装に多くの資金を投入しているので、非常に難しい問題に対して非常に優れたソリューションを実装することは驚くべきことではありません。しかし、JVMでお気に入りの言語をターゲットにすることはますます容易になっています。

+0

幸いにもこれは実際にはPythonの問題ではないようですが、悲惨なスレッディングをサポートしているので、最終的にはGILに肯定的です。(Jython/IPython/etcc以外の場合) – Basic

5

十分なメモリがある場合、ガベージコレクションは参照カウントよりも高速です(時間効率が向上します)。たとえば、コピーgcは「ライブ」オブジェクトを横断して新しい領域にコピーし、メモリ領域全体にマークを付けることで、すべての「死んだ」オブジェクトを1ステップで取り戻すことができます。これは非常に効率的ですの場合あなたは十分なメモリがあります。世代別コレクションは、「ほとんどのオブジェクトが若く死ぬ」という知識を使用します。多くの場合、オブジェクトのほんの数%がコピーされなければなりません。それはメモリ、それは到達不能取得する瞬間を再利用するので

[これはまた、GCはmalloc関数/自由よりも速くすることができる理由である]

参照カウントは、ガベージコレクションより効率的に非常に多くのスペースです。ファイナライザをオブジェクトに接続したい場合(Fileオブジェクトが到達できなくなったときにファイルを閉じるなど)、これは便利です。参照カウントシステムは、メモリのほんの数パーセントが空きであっても機能します。しかし、各ポインタ割り当て時にカウンタを増減させるための管理コストには多くの時間がかかり、サイクルを再生するために何らかの種類のガベージコレクションが必要です。

したがって、メモリが制約された環境で作業する必要がある場合、または精密なファイナライザが必要な場合は、参照カウントを使用します。十分なメモリがあり、速度が必要な場合は、ガベージコレクションを使用してください。

26

実際に参照カウントとSun JVMで使用される戦略は、すべて異なる種類のガベージコレクションアルゴリズムです。

死んだオブジェクトを追跡するための2つの広いアプローチがあります:トレースと参照カウント。トレースでは、GCはスタックリファレンスなどの "ルーツ"から開始し、すべての到達可能な(ライブ)オブジェクトをトレースします。到達できないものは死んだとみなされます。参照が変更されるたびに参照カウントでは、関連するオブジェクトのカウントが更新されます。参照カウントがゼロに設定されているオブジェクトはすべて、死んだとみなされます。基本的にすべてのGC実装ではトレードオフがあるが、トレースは通常、高スループット(すなわち高速)動作には良いが、長いポーズ時間(UIまたはプログラムがフリーズする可能性のある大きなギャップ)を有する。参照カウントは小さなチャンクで動作することができますが、全体的に遅くなります。それは凍結が少ないことを意味するかもしれませんが、パフォーマンスは全体的に劣ります。

さらに、参照カウントGCでは、参照カウントだけで捕捉されないサイクル内のオブジェクトをクリーンアップするサイクル検出器が必要です。 Perl 5はGC実装でサイクル検出器を持たず、周期的なメモリをリークすることがありました。

研究はまた、両方の長所(低休止時間、ハイスループット)を取得するために行われています:後期のゲームで http://cs.anu.edu.au/~Steve.Blackburn/pubs/papers/urc-oopsla-2003.pdf

1

を、私はPythonでRCのための1つの重要な根拠は、そのシンプルさだと思います。たとえば、email by Alex Martelliを参照してください。

(私はGoogleのキャッシュ外のリンクを見つけることができませんでした。電子メールの日付は2005年10月13日のpythonのリストです)。 JavaのトレースGCの

+1

間違ったリンクです。 – mtasic85

3

一つの大きな欠点は、時々、それは「世界を停止」とフルGCを行うには、比較的長い時間のためのアプリケーションを凍結するということです。ヒープが大きく、オブジェクトツリーが複雑な場合は、数秒間フリーズします。また、完全な各GCはオブジェクトツリー全体を何度も繰り返し訪問しますが、これはおそらく非常に非効率的です。 JavaがGCを行う方法のもう一つの欠点は、jvmにあなたが望むヒープサイズを伝えなければならないということです(デフォルトが十分でない場合)。 JVMはその値からヒープにゴミが積み重なっているときにGCプロセスをトリガーするいくつかのしきい値を導出します。

これは、実際には、最も高価な携帯電話でも、ObjectiveCに基づいた、RCを使用したiOSの滑らかさと比べて、Android(Javaベース)のぎざぎざの感じの主な原因だと推測します。

私は、RCのメモリ管理を有効にするには、JVMオプションを見るのが大好き、そして残された多くのメモリがない場合、多分、最後の手段としてのみ実行するようGCを維持するだろう。

+0

Java GC(ガベージコレクション)は、GCの実行時に実行をフリーズします。したがって、実行を一時停止することが許容できないすべての時間に応答する必要があるアプリケーションには適していません。 RC(Reference counting)は、リンゴデバイスで使用されるObjective CとSwiftで使用されます。だから、iOSはもっと反応しているように感じます。 python、ObjectiveC、Swiftなどの短いRC言語では、予測可能なパフォーマンスがあり、GCを実行するために一時停止しません。 Javaはまた、ヒープのためにあまりにも多くのメモリを必要とするため、実行に必要なだけの量のメモリをPythonが使用するため、他のアプリケーションに使用可能なRAMがないという欠点があります。 – codefire

+0

http://patshaughnessy.net/2013/10/24/visualizing-garbage-collection-in-ruby-and-python – codefire

+0

@codefireいくつかのトレースgcの実装は、世界を停止させません。 Azul Zing JVMはこれを実装しています。アルゴリズムは次のとおりです。http://www.azulsystems.com/sites/default/files/images/c4_paper_acm.pdf –

関連する問題