이 블로그는 개인의 공부 목적으로 작성된 블로그입니다. 왜곡된 정보가 포함되어 있을 수 있습니다
7. 다 쓴 객체 참조를 해제하라
자바에서는 garbage collection이라는 메모리 해제 툴로 인해 사용한 객체 해제에 대해 별로 신경 쓰지 않는다. 책에서는 메모리 해제에 대해 어느정도 주의할 것을 요구하고 있다. 다음 코드를 보자
public class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack() {
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
public void push(Object e) {
ensureCapacity();
elements[size++] = e;
}
public Object pop() {
if (size == 0) {
throw new EmptyStackException();
}
return elements[--size];
}
private void ensureCapacity() {
if (elements.length == size) {
elements = Arrays.copyOf(elements, 2 * size + 1)
}
}
}
스택을 구현한 클래스이다. 위 클래스는 메모리 누수를 발생시킬 수 있는 여지가 있는데, 스택에서 pop되는 경우 해당하는 인스턴스 메모리 해제가 이루어지지 않아 메모리 누수가 발생한다. (garbage collecttion 입장에서는 아직 사용중인 인스턴스이다.) 아주 간단한 해결 방법은 pop할때, 해당 인덱스를 null로 초기화 하면 된다. (아무도 참조하지 않는 인스턴스는 GC가 찾을 수 있기 때문에)
public Object pop() {
if (size == 0) {
throw new EmptyStackException();
}
Object result=elements[--size];
elements[size]=null;
return result;
}
책에서는 null 처리하는 것 보다 해당 변수를 유효 번위 밖으로 밀어내는 방법을 추천하고 있다.(이 방법은 item 8에서 제시됨)
메모리를 직접 관리하는 클래스의 경우에 메모리 누수를 주의해야한다. 위의 경우 stack이라는 메모리 자료구조를 구현한것이 예이다.
캐시 또한 메모리 누수를 일으키는 주범이다. WeakHashMap을 사용하여 캐시를 만드는 것을 권장한다(다 사용했을 경ㅇ우 즉시 제거)
8. finalizer와 cleaner 사용을 피하라
자바에는 finalizer, cleaner 라는 2가지 객체 소멸자가 존재한다. 전자의 경우 GC에서 인스턴스를 제거하기 전에 호출하는데 인스턴스 내부에 있는 리소스를 해제한다고 한다. 그러나 finalizer는 예측할수 없는 등 다양한 문제가 있어 사용을 피해야한다고 한다.(실제로 자바에서도 deprecated으로 지정되어 있음)
cleaner는 finalizer보다 덜 위험하지만, 예측할 수 없는 등 비슷한 문제가 존재하여 결국 둘다 쓰지 말라는 것이 책의 의견이다.
가장 큰 문제는 두가지 소멸자 모두 언제 호출되는지, 호출이 되는지(호출자체가 안될 수 있음) 보장이 되지 않아(그이유로 책에서 우선순위가 낮아 다른 스레드에게 밀린다는 뉘양스이다) 영구적으로 수정하는 작업에 사용해선 안된다는 것이다.
다음 코드를 보자
public class Room implements AutoCloseable{
private static final Cleaner cleaner=Cleaner.create();
private static class State implements Runnable{
int numJunkPiles;
State(int numJunkPiles){
this.numJunkPiles=numJunkPiles;
}
@Override
public void run(){
System.out.println("방 청소");
}
}
private final State state;
private final Cleaner.Cleanable cleanable;
public Room(int numJunkPiles){
state=new State(numJunkPiles);
cleanable=cleaner.register(this,state);
}
@Override
public void close(){
cleanable.clean();
}
}
AutoCloseable를 활용하여 cleaner를 안전망으로 사용한 클래스로, run 메서드가 cleanable에 의해 딱 한번 호출되고, run 메서드가 Room이 close메서드를 호출할때, GC가 Room을 회수할때 호출된다.
public class Adult {
public static void main(String[] args) {
try(Room myRoom =new Room(7)){
System.out.println("hi");
}
}
}
위코드는 try-with-resources 을 사용한것
public class Teenager {
public static void main(String[] args) {
new Room(99);
System.out.println("i'm teenage");
}
}
반대로 위코드는 예측할수 없는 상황으로 run이 호출되지 않는다.
try-finally 보다는 try-with-resources
close메서드를 호출해야하는 경우, finalizer,try-finally보다 try-with-resource를 사용하는 것을 책에서 제시하고 있다.
try-with-resources를 사용하기 위해서는 AutoCloseable를 구현해야한다.
'Java' 카테고리의 다른 글
[이펙티브 자바] 아이템 47, 48, 49 (2) | 2024.04.27 |
---|---|
[이펙티브 자바] 아이템 39, 40, 41 (1) | 2024.03.24 |
[이펙티브 자바] 아이템 10, 11 (1) | 2024.01.14 |
[이펙티브 자바] 아이템 3,4,5,6 (0) | 2024.01.07 |
[이펙티브 자바] 아이템 1, 2 (0) | 2024.01.04 |