6

私はコンパイラに関する大学のコースを受講していて、ガベージ コレクションとメモリを解放する方法についての話を終えたところです。しかし、クラスの講義や教科書で、参照カウントはメモリを管理する優れた方法ではないと信じるようになりました。

その理由は、プログラムが参照カウントをインクリメントおよびデクリメントするために多数の追加命令を挿入する必要があるため、参照カウントは非常に高価であるというものでした。さらに、参照カウントが変更されるたびに、プログラムはそれがゼロに等しいかどうかを確認し、そうであればメモリを再利用する必要があります。

私の教科書には、「全体として、参照カウントの問題はその利点よりも重要であり、プログラミング言語環境で自動ストレージ管理に使用されることはめったにありません。

私の質問は次のとおりです。これらは正当な懸念ですか? Objective-C は何らかの方法でそれらを回避しますか? もしそうなら、どのように?

4

3 に答える 3

6

参照カウントには意味のあるオーバーヘッドがあります。それは本当です。ただし、ガベージ コレクタをトレースする「古典的な教科書」ソリューションにも欠点がないわけではありません。最大のものは非決定論ですが、一時停止とスループットも大きな懸念事項です。

しかし、結局のところ、ObjC には選択肢がありません。最先端のコピー コレクターには、ObjC にはない言語の特定のプロパティ (たとえば、生のポインターは必要ありません) が必要です。その結果、教科書の解決策を ObjC に適用しようとすると、部分的に保守的でコピーを行わないコレクターが必要になります。これは、実際には refcounting とほぼ同じ速度ですが、決定論的な動作はありません。

(編集) 私の個人的な感覚では、スループットは 2 番目または 3 番目の懸念事項であり、本当に重要な議論は決定論的な動作とサイクル コレクションおよびコピーによるヒープ圧縮に帰着します。これら 3 つすべてが非常に貴重なプロパティであるため、1 つを選択するのは難しいでしょう。

于 2013-05-01T18:23:17.077 に答える
1

コンピューター サイエンスの研究における RC とトレーシングのコンセンサスは、長い間 (最大の) 一時停止時間にもかかわらず、トレーシングが優れた CPU スループットを持っているというものでした。(例: herehere、およびhereを参照してください。) ごく最近の 2013 年に、テスト済みの最高のトレース GC と同等またはわずかに優れた RC ベースのシステムを提示する論文 (これら 3 つの最後のリンク) が発表されました。 CPU スループットに関して。言うまでもなく、「実際の」実装はまだありません。

これは、iOS 7.1 64 ビット シミュレーターで、3.1 GHz i5 を搭載した iMac で行ったばかりの小さなベンチマークです。

long tenmillion = 10000000;
NSTimeInterval t;

t = [NSDate timeIntervalSinceReferenceDate];
NSMutableArray *arr = [NSMutableArray arrayWithCapacity:tenmillion];
for (long i = 0; i < tenmillion; ++i)
    [arr addObject:[NSObject new]];
NSLog(@"%f seconds: Allocating ten million objects and putting them in an array.", [NSDate timeIntervalSinceReferenceDate] - t);

t = [NSDate timeIntervalSinceReferenceDate];
for (NSObject *obj in arr)
    [self doNothingWith:obj]; // Can't be optimized out because it's a method call.
NSLog(@"%f seconds: Calling a method on an object ten million times.", [NSDate timeIntervalSinceReferenceDate] - t);

t = [NSDate timeIntervalSinceReferenceDate];
NSObject *o;
for (NSObject *obj in arr)
    o = obj;
NSLog(@"%f seconds: Setting a pointer ten million times.", [NSDate timeIntervalSinceReferenceDate] - t);

ARC を無効にすると ( -fno-objc-arc)、次のようになります。

2.029345 seconds: Allocating ten million objects and putting them in an array.
0.047976 seconds: Calling a method on an object ten million times.
0.006162 seconds: Setting a pointer ten million times.

ARC を有効にすると、次のようになります。

1.794860 seconds: Allocating ten million objects and putting them in an array.
0.067440 seconds: Calling a method on an object ten million times.
0.788266 seconds: Setting a pointer ten million times.

オブジェクトの割り当てとメソッドの呼び出しがいくらか安くなったようです。オブジェクト ポインターへの代入は桁違いに高価になりましたが、非 ARC の例では -retain を呼び出さなかったことを忘れないでください__unsafe_unretained。クレイジー。それにもかかわらず、メモリ管理を「忘れて」、ARC が必要な場所に保持/解放呼び出しを挿入できるようにしたい場合は、一般的に、ポインタを設定するすべてのコード パスで繰り返し、多くの CPU サイクルを浪費することになります。一方、トレース GC はコード自体を放置し、特定の瞬間 (通常は何かを割り当てるとき) にのみ起動し、一気に処理を行います。(もちろん詳細はいろいろ世代別 GC、インクリメンタル GC、コンカレント GC などを考えると、実際にはもっと複雑です)。

そうです、Objective-C の RC はアトミックな保持/解放を使用するため、かなりコストがかかりますが、Objective-C には、refcounting によって課せられるものよりも多くの非効率性もあります。(たとえば、実行時にいつでも「スウィズル」できるメソッドの完全に動的/反射的な性質により、コンパイラは、データフロー分析などを必要とする多くのクロスメソッド最適化を実行できなくなります。 objc_msgSend( ) は常に、静的アナライザーの観点からは「動的にリンクされた」ブラック ボックスへの呼び出しです。) 全体として、言語としての Objective-C は、正確に最も効率的または最適化可能な言語ではありません。人々はそれを「Smalltalk の猛烈な速度を備えた C の型安全性」と呼んでいますが、それには理由があります。;-)

Objective-C を作成するときは、一般的に、適切に実装された Apple ライブラリを中心にインストルメント化するだけです。これらのライブラリは、C や C++、アセンブリなどをホットスポットに確実に使用します。独自のコードが効率的である必要はほとんどありません。ホット スポットがある場合、単一の Objective-C メソッド内の純粋な C スタイル コードのような下位レベルの構造にドロップダウンすることで、非常に効率的にすることができますが、これが必要になることはめったにありません。これが、Objective-C が一般的なケースで ARC のコストを負担できる理由です。GC のトレースに固有の機能があるとはまだ確信していません。メモリに制約のある環境での問題を解決し、適切な高級言語を使用して上記のライブラリを同様に計測できると考えていますが、どうやら RC は Apple/iOS の方が適しているようです。なぜトレーシング GC を使用しなかったのかを自問するときは、これまでに構築したフレームワーク全体とすべてのレガシー ライブラリを考慮する必要があります。たとえば、RC は CoreFoundation にかなり深く組み込まれていると聞いたことがあります。

于 2014-08-14T17:19:35.047 に答える
0

全体として、参照カウントの問題はその利点を上回り、プログラミング言語環境で自動ストレージ管理に使用されることはめったにありません

ややこしい言葉はautomatic

物事を行うための伝統的な Obj-C の方法である手動参照カウントは、問題をプログラマーに委任することで問題を回避します。プログラマーは、参照カウントについて理解し、手動で追加retainおよびrelease呼び出しを行う必要があります。彼/彼女が参照サイクルを作成した場合、彼/彼女はそれを解決する責任があります。

最新の自動参照カウントは、プログラマーにとって多くのことを行いますが、それでも透過的なストレージ管理ではありません。プログラマーは参照カウントについて知っておく必要があり、参照サイクルを解決する必要があります。

本当にトリッキーなのは、透過的に参照カウントによってメモリ管理を処理するフレームワークを作成することです。つまり、プログラマがそれについて知る必要はありません。そのため、自動ストレージ管理には使用されません。

追加の命令によるパフォーマンスの低下はそれほど大きくなく、通常は重要ではありません。

于 2013-05-01T18:41:37.220 に答える