OCのisaポインタにはどのような情報が格納されていますか?
3497 ワード
OC内のすべてのインスタンスオブジェクト、クラスオブジェクト、およびメタクラスオブジェクトには、
しかし、現在の64ビットシステム(arm 64アーキテクチャ)では、アップルは
上記の情報で定義された
isa
というメンバー変数があり、通常はisa
と呼ばれ、ポインタである以上、そこに格納されているのはアドレスであるべきである.従来の32
システムでは、isa
は確かに格納されたアドレスであり、インスタンスオブジェクトのisa
は対応するクラスオブジェクトのアドレスを格納し、クラスオブジェクトのisa
は対応するメタオブジェクトのアドレスを格納し、メタオブジェクトのisa
はルートメタオブジェクトのアドレスを格納していた.しかし、現在の64ビットシステム(arm 64アーキテクチャ)では、アップルは
isa
を最適化し、1つのアドレスのほかに多くの情報を格納している.1つのポインタは8バイト、すなわち64ビットを占め、アップルはそのうちの33
だけでアドレスを格納し、残りの31ビットは他の情報を格納するために使用される.次に、arm64
アーキテクチャにおけるisaの定義を見てみましょう.# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 19
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
上記の情報で定義された
ISA_MASK
のような定数は、isa
を操作する過程でプログラムが使用するものであり、例えば、isa
とISA_MASK
をビットと演算isa & ISA_MASK
でisaに格納されたアドレス値を得ることができる.uintptr_t
のデータに注目します.nonpointer
:(isaの0位(isaの一番後ろの方)、合計1位).0はこのisa
がアドレス値のみを格納していることを示し、1はこれが最適化されたisa
であることを示している.has_assoc
:(isaの1位、合計1位).このオブジェクトが関連オブジェクトかどうかを記録し、なければ解放が速くなります.has_cxx_dtor
:(isaの2位、合計1位).c++の構造関数があるかどうかを記録し、なければ解放が早い.shiftcls
:(isaの3-35位、計33位).クラスオブジェクトまたはメタオブジェクトのアドレス値を記録します.magic
:(isaの36-41ビット目、合計6ビット)は、デバッグ時にオブジェクトの初期化が完了したかどうかを判別するために使用されます.weakly_referenced
:(isaの42位、合計1位)は、そのオブジェクトが弱い参照または弱い参照を受けたことがあるかどうかを記録するために使用され、弱い参照を受けていないオブジェクトはより速く解放されることができる.deallocating
:(isaの43番目、合計1ビット)は、オブジェクトがメモリを解放しているかどうかを示します.has_sidetable_rc
:(isaの44番目、合計1ビット)は、拡張された参照カウントがあるかどうかをマークするために使用されます.1つのオブジェクトの参照カウントが比較的少ない場合、その参照カウントはisa
に記録され、参照カウントがある値より大きい場合、sideTable
が参照カウントの格納を支援する.extra_rc
:(isaの45-63ビット目、合計19ビット)は、そのオブジェクトの参照カウント値-1(例えば、参照カウントが5であればここでは4)を記録するために使用される.ここでは合計19ビットであり、参照カウントが大きい場合、19ビットが残らない場合はsideTable
を用いて記憶を支援する.ルールは、19ビットが満杯になると、19ビットの半分(すなわち、上記で定義したRC_HALF
)がsideTable
に格納され、このとき参照カウントが+1になるとextra_rc
に加算され、extra_rc
が満杯になるとRC_HALF
の大きさを出し続けてsideTable
に格納される.参照カウントが減少すると、extra_rc
の値が0に減少すると、sideTable
からRC_HALF
のサイズが取り出されてextra_rc
に入る.以上説明したように、参照カウントは、extra_rc
を直接操作することなく、増加または減少することがsideTable
で行われる.これは、sideTable
にスピンロックがあり、参照カウントの増加および減少操作が非常に頻繁であり、sideTable
を直接操作すると性能に非常に影響を及ぼすため、sideTable
へのアクセスを最小限に抑えるように設計されている.