![]() |
参照カウント(さんしょうカウント、英: reference counting)は、メモリオブジェクトのライフサイクル(寿命)管理に使用される方式のひとつ。ガベージコレクションの実装方法およびガベージコレクタの動作方法のひとつとしても利用される。また、コピーオンライトの実装方法としても多用される。
共有された単一のオブジェクトへの参照ではなく、独立したデータを擬似的に表現する場合は、下記の処理を追加する。
また、システムの空き時間(アイドル時)に動作する方式のガベージコレクションとは異なり、参照カウント方式は通例決定論的 (deterministic) に動作するため、オブジェクトの解放タイミングを確実に制御したい場合に有利である。
特にマルチスレッド環境で参照カウントを利用する場合、スレッド間で共有されるオブジェクトのライフサイクルを正しく安全に管理するためには参照カウントの増減がスレッドセーフになるよう配慮しなければならないが、排他制御やアトミック操作などのロック機構は想定以上にコストの高い処理であり、大量に実行される場合は大きなオーバーヘッドを伴う[1]。
文字列オブジェクトの実装手段としては適しており、多くのシステムで採用されている。 これは、文字列であれば内部で他のオブジェクトを参照しないので、短所の多くには影響がないためである。
単純なビット列などのデータも同様に適している。
参照カウントには、循環参照によりデータを解放できなくなる(メモリリークが発生する)という問題がある。
例えばC++向けのBoost C++ライブラリあるいはC++11規格以降の標準C++ライブラリには、参照カウントベースのスマートポインタを実現するクラステンプレートとして、それぞれboost::shared_ptr
あるいはstd::shared_ptr
が用意されているが、これを使って自己参照あるいは相互参照を持つクラスを定義してしまうと、参照カウントが適切に減らされずに互いのインスタンスが解放されなくなってしまう。
#include <iostream>
#include <memory>
class A {
public:
std::shared_ptr<class B> m_refB;
A() {}
~A() {
std::cout << "Destructor of A is called." << std::endl;
}
};
class B {
public:
std::shared_ptr<class A> m_refA;
B() {}
~B() {
std::cout << "Destructor of B is called." << std::endl;
}
};
void doTest() {
std::shared_ptr<A> refA(new A());
std::shared_ptr<B> refB(new B());
refA->m_refB = refB;
refB->m_refA = refA;
} // ここで A および B のデストラクタが呼ばれなくなり、メモリリークする。
int main() {
doTest();
return 0;
}
循環参照によるメモリリークを回避するためには、利用を終えたタイミングで明示的に参照を解除するコードを記述するなどの方法があるが、ソースコードが煩雑になってしまい、スマートポインタの利点を打ち消してしまう。
この問題を解消するために、多くのプログラミング言語やソフトウェアライブラリあるいはシステムで弱い参照 (ウィークリファレンス、英: weak reference) が導入されている。弱い参照とは、参照カウントを増加させない参照である。例えばC++ではboost::weak_ptr
あるいはstd::weak_ptr
などが該当する。
一方、マーク・アンド・スイープやコピーGCによるガベージコレクションでは、循環参照によるメモリリークの問題は発生しない。
なお、Javaや.NET Frameworkでは、いずれも参照カウントベースではないガベージコレクション方式を採用しており[2][3][4][5]、循環参照によるメモリリークは発生しない。例えば以下のJavaコードは合法であり、循環参照していたとしてもガベージコレクションの回収対象となる。
class A {
public B b;
}
class B {
public A a;
}
public class Test {
public static void doTest() {
A a = new A();
B b = new B();
a.b = b;
b.a = a;
} // 十分な時間が経過すれば、いずれ GC により回収される。
public static void main(String[] args) {
doTest();
}
}
ただし、非意図的オブジェクト保持(unintentional object retention)が引き起こすメモリリークを回避するために、通常の参照(強参照)の代わりにjava.lang.ref.WeakReference
のような弱参照クラスのインスタンスを使うこともある[6]。
ウィキペディアの「孤立した記事」は、参照カウントが0のものを表示しているだけなので、孤立した記事だけから参照されている記事は孤立した記事と見なされていない。
std::shared_ptr
が存在する。boost::shared_ptr
およびboost::shared_array
[注釈 1]。boost::intrusive_ptr
もある。