Item 7. 書き上げたオブジェクトの参照を解除


Javaでは、メモリを管理するゴミ回収(GC)が存在し、ユーザのメモリ管理の負担が軽減される.
ただしGCはすべてのメモリを管理することはできません.自分が直接メモリを管理しているクラスについてはGCの管理から外れてしまいますが、クラス内部のメモリ漏れを防ぐためには防犯が必要です.
簡単なメモリリークスタッククラスを次に示します.
public class Stack{
    private Object[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;
    
    public Stack(){
    	return elements = new Object[DEFAULT_INITIAL_CAPACITY];
    }
    
    pulbic void push(Object e){
    	ensureCapacity();
    	elemetes[size++] = e;
    }
    
    public Obejct pop(){
    	if( size == 0 )
        	throw new EmptyStackException();
        return elementes[--size];
    }
    
    private void ensureCapacity(){
    // 공간을 두 배로 늘린 후, 복붙
    	if(elements.length == size)
        	elements = Array.copyOf(elements, 2 * size +1);
    }

}
一見問題ない.しかし、よく見るとメモリ漏れの危険性がわかります.では、どこでメモリ漏れが発生しますか?
ちょうどpop()をする時です.上のコードから見るとpop()と簡単にsizeを縮小する操作しかしません.size内部のデータは、使用する領域が「アクティブ領域」であることを示す.アクティブな領域の外にすべてのデータの参照(古い参照)を書き込みます.書き終わった参照はまだ存在するのでnull処理を行う必要があります.
pop()の変更
public Object pop(){
    if(size==0)
    	throw new EmptyStackException();
	Object result = elements[--size];
    elements[size] = null;   // 다 쓴 참조 객체
    return result;
}
書き終わったオブジェクトを残してうっかりアクセスすると、NullPointerExceptionを投げ出してすぐにプログラムを終了します.
では、すべてのオブジェクトは書き終わるとnull処理をしますか?そうではありません.このようにnull処理を1つずつ行うと,コードが乱雑になる.
可能であれば、有効範囲(scope)の外に出して、いっそ参考にさせないほうがいいです.
以上のように、メモリを直接管理するクラスはGCの管理から外れますので、常にメモリリークに注意してください.
また、メモリ漏洩の1つの原因はキャッシュです.オブジェクトリファレンスをキャッシュに忘れた場合は、メモリ漏洩のトリガポイントが保持されているのと同じです.
対応策はWeakHashMapを使用してキャッシュを作成することです.
弱参照(Weak Reference)として作成し、GCの領域に配置します.
3つ目はリスナーとコールバックです.クライアントはコールバックを登録していますが、コールバックは明確に解除されないため、コールバックは蓄積されます.この場合、弱い参照としてコールバックに格納されると、GCは直ちに回収される.