아이템 7 - 다 쓴 객체 참조를 해제하라
자바처럼 가비지 컬렉터를 갖춘 언어를 쓰게되면 자칫 메모리 관리에 더 이상 신경 쓰지 않아도 된다고 오해할 수 있는데, 절대 사실이 아니다.
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);
}
public static void main(String[] args) {
Stack stack = new Stack();
for (String arg : args)
stack.push(arg);
while (true)
System.err.println(stack.pop());
}
}
이 코드에서 메모리 누수를 어디서 일어날까?
이 코드에서는 스택이 커졌다가 줄어들었을 때 스택에서 꺼내진 객체들을 가비지 컬렉터가 회수하지 않는다. 프로그램에서 그 객체들을 더 이상 사용하지 않더라도 말이다.
기존의 pop() 메서드
public Object pop() {
if (size == 0)
throw new EmptyStackException();
return elements[--size];
}
이 메서드는 스택에서 가장 최근에 추가된 객체를 반환하고, 스택의 크기를 감소시킨다.
하지만 반환한 객체에 대한 참조는 여전히 elements 배열에 남아 있어, 사용 후에도 메모리에서 해제되지 않는다. 이는 메모리 누수를 유발할 수 있다.
개선된 pop() 메서드
public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // 다 쓴 참조 해제
return result;
}
이 메서드는 먼저 스택이 비어 있는지 확인하고, 비어 있다면 EmptyStackException을 던진다.
스택에서 가장 최근에 추가된 객체를 result에 저장한 후, 해당 객체에 대한 참조를 elements 배열에서 null로 설정한다. 이를 통해 메모리 누수를 방지할 수 있다.
- 일반적으로 자기 메모리를 직접 관리하는 클래스라면 프로그래머는 항싱 메모리 누수에 조심해야한다.
- 캐시 역시 메모리 누수를 일으키는 주범이다. → WeakHashMap을 사용하여 캐시를 만드는 방법으로 해결할 수 있다.
핵심 정리
메모리 누수는 겉으로 잘 드러나지 않아 시스템에 수년간 잠복하는 사례도 있다. 이런 누수는 철저한 코드 리뷰나 힙 프로파일러 같은 디버깅 도구를 동원해야만 발견되기도 한다. 그래서 이런 종류의 문제는 예방법을 익혀두는 것이 매우 중요하다.
'Java' 카테고리의 다른 글
이펙티브 자바(Effective Java) 2장 - 아이템 9 try-finally보다는 try-with-resources를 사용하라 (0) | 2024.12.03 |
---|---|
이펙티브 자바(Effective Java) 2장 - 아이템 8 finalizer와 cleaner 사용을 피하라 (0) | 2024.12.03 |
이펙티브 자바(Effective Java) 2장 - 아이템 6 불필요한 객체 생성을 피하라 (0) | 2024.12.03 |
이펙티브 자바(Effective Java) 2장 - 아이템 5 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (0) | 2024.12.03 |
이펙티브 자바(Effective Java) 2장 - 아이템 4 인스턴스화를 막으려거든 private 생성자를 사용하라 (0) | 2024.12.03 |
아이템 7 - 다 쓴 객체 참조를 해제하라
자바처럼 가비지 컬렉터를 갖춘 언어를 쓰게되면 자칫 메모리 관리에 더 이상 신경 쓰지 않아도 된다고 오해할 수 있는데, 절대 사실이 아니다.
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); } public static void main(String[] args) { Stack stack = new Stack(); for (String arg : args) stack.push(arg); while (true) System.err.println(stack.pop()); } }
이 코드에서 메모리 누수를 어디서 일어날까?
이 코드에서는 스택이 커졌다가 줄어들었을 때 스택에서 꺼내진 객체들을 가비지 컬렉터가 회수하지 않는다. 프로그램에서 그 객체들을 더 이상 사용하지 않더라도 말이다.
기존의 pop() 메서드
public Object pop() { if (size == 0) throw new EmptyStackException(); return elements[--size]; }
이 메서드는 스택에서 가장 최근에 추가된 객체를 반환하고, 스택의 크기를 감소시킨다.
하지만 반환한 객체에 대한 참조는 여전히 elements 배열에 남아 있어, 사용 후에도 메모리에서 해제되지 않는다. 이는 메모리 누수를 유발할 수 있다.
개선된 pop() 메서드
public Object pop() { if (size == 0) throw new EmptyStackException(); Object result = elements[--size]; elements[size] = null; // 다 쓴 참조 해제 return result; }
이 메서드는 먼저 스택이 비어 있는지 확인하고, 비어 있다면 EmptyStackException을 던진다.
스택에서 가장 최근에 추가된 객체를 result에 저장한 후, 해당 객체에 대한 참조를 elements 배열에서 null로 설정한다. 이를 통해 메모리 누수를 방지할 수 있다.
- 일반적으로 자기 메모리를 직접 관리하는 클래스라면 프로그래머는 항싱 메모리 누수에 조심해야한다.
- 캐시 역시 메모리 누수를 일으키는 주범이다. → WeakHashMap을 사용하여 캐시를 만드는 방법으로 해결할 수 있다.
핵심 정리
메모리 누수는 겉으로 잘 드러나지 않아 시스템에 수년간 잠복하는 사례도 있다. 이런 누수는 철저한 코드 리뷰나 힙 프로파일러 같은 디버깅 도구를 동원해야만 발견되기도 한다. 그래서 이런 종류의 문제는 예방법을 익혀두는 것이 매우 중요하다.
'Java' 카테고리의 다른 글
이펙티브 자바(Effective Java) 2장 - 아이템 9 try-finally보다는 try-with-resources를 사용하라 (0) | 2024.12.03 |
---|---|
이펙티브 자바(Effective Java) 2장 - 아이템 8 finalizer와 cleaner 사용을 피하라 (0) | 2024.12.03 |
이펙티브 자바(Effective Java) 2장 - 아이템 6 불필요한 객체 생성을 피하라 (0) | 2024.12.03 |
이펙티브 자바(Effective Java) 2장 - 아이템 5 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (0) | 2024.12.03 |
이펙티브 자바(Effective Java) 2장 - 아이템 4 인스턴스화를 막으려거든 private 생성자를 사용하라 (0) | 2024.12.03 |