参照型変数
Javaでは全てのデータやメソッドはクラスに属し、原則としてインスタンスを生成して利用すると学びました。
では、インスタンスは作成された後はどうなるのでしょう?
参照
もし、同じメソッドを何度も使いたい場合でもその都度インスタンスを生成しなければならないとしたならば、非常に効率が悪いと言えます。
(new HelloWorld()).helloWorld(); (new HelloWorld()).helloWorld(); (new HelloWorld()).helloWorld();
よって、インスタンスを再利用する為の仕組みが必要です。
Javaでは変数にインスタンスを格納することができ、参照型変数と呼びます。
上記のコードは参照型変数を使用すると次のように書き換えることができます。
HelloWorld hello = new HelloWorld();
hello.helloWorld();
hello.helloWorld();
hello.helloWorld();
1行目ではHelloWorldクラスのインスタンスが生成されますが、それはHelloWorldの参照型変数helloに代入されます。
2行目以降では、参照型変数helloに対して、メソッドを呼び出されています。
この時、HelloWorldのインスタンスは1回しか生成されていませんので、メモリの領域も1つ分で済みます。
この仕組みはC言語のポインタの仕組みと似ています。
C言語のポインタではメモリ上のアドレスとサイズ(型)を情報としてポインタ変数に持たせ、間接的にデータ(メモリ)にアクセスする仕組みでしたが、Javaの参照ではクラスの情報を参照型変数に持たせ、間接的にインスタンス(メモリ)へアクセスします。
しかし、大きく異なるのはクラスの情報にはメソッドの情報も含まれる点です。
参照の代入
参照型変数には代入演算子(=)を使用することで参照を代入することができます。
以下の例を考えて見ましょう。
HelloWorld hello1 = new HelloWorld();
HelloWorld hello2 = hello1;
インスタンスは1つしか生成されていませんが、参照型変数は2つ宣言されています。
このように参照型変数に参照型変数を代入すると、代入された参照型変数と同じインスタンスを指す事になります。
参照型変数の代入は一般的に次のように記述します。
参照型変数1 = 参照型変数2;
この時、参照型変数の参照先アドレスはコピーされています、インスタンスはコピーされていない事に注意してください。
ガベージコレクション
C言語においてはメモリの操作が自由自在であったことですが、それは諸刃の剣でした。
完璧にメモリ管理が実現されているのであればメモリ効率・実行効率など全てにおいて最高のパフォーマンスを発揮します。
また、OSやドライバのようなハードウェアに近いソフトウェアを開発するにはメモリ操作は必須となります。
しかし、プログラマはメモリを厳密に管理することを求められ、ちょっとしたメモリの開放忘れがバグ(メモリリーク)となります。
そして、システム全体に障害を引き起こすことも珍しい話ではなく、なによりも判明しにくく再現性も低い事が悩みの種でした。
Javaではこのようなメモリ管理をJVMが自動的に行う仕組みが提供されています。
また、プログラマはメモリ管理を意識できないどころか、できないように制限されています。
JVMは実行時にインスタンスが生成されるとメモリ領域(正確にはヒープ領域)が確保します。
その後、必要がなくなったとJVMが判断するとガベージコレクション(Garbage Collection、以下GC)により自動的にメモリは開放されます。
GCはプログラマが明示的に実行させることはできません。
このようにメモリ管理はJVMが自動的に行うので意識する必要はないのですが、どのような条件でGCが実行されるのかは理解しておく必要があります。
GC対象となるインスタンス
GCの対象となるインスタンスはどこからも参照されなくなったインスタンスです。
使い捨ての場合
1行限りでインスタンスが生成され、参照型変数に代入されないケースです。
次のようなコードが相当するでしょう。
(new HelloWorld()).helloWorld();
このようなコードの場合、インスタンスはその後のコードで使用することはできません。
よってすぐにでもメモリから開放しても問題ありませんから、即座にGCの対象となります。
繰り返しますが、この行が終わったならば即座にGCが走るわけではありません。
あくまでGCの対象となるだけということを忘れないでください。
参照が切れた場合
参照型変数に代入されてもインスタンスへの参照がなくなってしまうケースがあります。
次のようなコードが相当するでしょう。
HelloWorld hello1 = new HelloWorld(); HelloWorld hello2 = new HelloWorld(); hello2 = hello1;
このコードでは2つのインスタンスが生成され、それぞれhello1, hello2で参照されます。
しかし、hello2の参照はhello1の参照するインスタンスとなる為、hello2が参照していたインスタンスはどこからも参照されなくなりました。
このように参照がなくなった場合、この後のコードで2つ目のインスタンスを参照する術がない為、GCの対象となります。
尚、インスタンスが参照を失った状態は「インスタンスがデッド(死んでいる)」であると言われ、逆にインスタンスが参照を持っている状態は「インスタンスがライブ(生きている)」であると言われます。
言い換えれば、デッドなインスタンスがGCの対象となります。