📖본 포스팅은 'Effective java - 조슈아' 님의 책를 보고 작성되었습니다.
[자바의 가비지 컬렉터?]
[가비지 컬렉터(가비지 컬렉션)]
쓰레기 수집(garbage collection 가비지 컬렉션, GC)은 메모리 관리 기법 중의 하나로, 프로그램이 동적으로 할당했던 메모리 영역 중에서 필요없게 된 영역을 해제하는 기능이다. 영어를 그대로 읽어 가비지 컬렉션이라 부르기도 한다.
📌출처: https://ko.wikipedia.org/wiki/%EC%93%B0%EB%A0%88%EA%B8%B0_%EC%88%98%EC%A7%91_(%EC%BB%B4%ED%93%A8%ED%84%B0_%EA%B3%BC%ED%95%99)
- c언어 c++언어의 경우엔 메모리를 직접 관리해야 하기 때문에 자바처럼 가비지 컬렉터를 갖춘 언어로 넘어오면 프로그래머의 삶이 평안해진다.
- 자바의 경우엔 다 쓴 객체를 가비지 컬렉터가 알아서 회수해간다.
- 위의 사실만 본다면 메모리 관리를 신경쓰지 않아도 될것 같지만 그렇지 않다. 자바 역시도 다 쓴 객체가 있다면 참조를 해제해주어야 메모리 낭비를 막을 수 있다.
[잡기 힘든 메모리 누수]
package hello.springmvc;
import java.util.Arrays;
import java.util.EmptyStackException;
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);
}
}
[메모리 누수를 막을 방법은?]
- 해당 참조를 다 썼을 때 null처리(=참조 해제)를 해주면 된다.
- 위의 코드의 경우 null 처리(=참조 해제)를 해주는 타이밍은 스택 클래스에서 각 원소의 참조가 더 이상 필요 없어지는 시점이 스택에서 꺼내질 때고 그때 참조 해제를 해주면 된다.
public Object pop(){
if (size==0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null;
return result;
}
- 스택에서 해당 값을 빼게 되면 더이상 그 객체는 필요하지 않기 때문에 null 처리(=참조 해제)를 해준 뒤 새로운 값을 돌려주면 된다.
[그러나 객체 참조를 null 처리하는 예외적인 경우어야 한다.]
- 다쓴 객체 참조를 null처리 하는 것보다 그 참조 변수를 담은 변수를 유효 범위(scope) 밖으로 밀어내는 것이 가장 좋은 객체 참조 해제 방법이다.
- 변수의 범위를 최소가 되게 정의했다면 이 일은 매우 자연스러워진다.(아이템 57에서 이 이야기를 다룸)
- null 처리를 하는 예외적인 경우는?
- Stack과 같은 자료구조는 메모리를 본인이 직접 관리하기 때문에 메모리 누수에 굉장히 취약하다. 이는 결국 활성 영역과 비활성 영역에 관해서 프로그래머만 아는 사실이기에 이를 관리하기 위해서는 다 쓴 객체 참조를 null 처리를 이용해서 다시는 쓰지 않음을 가비지 컬렉터에 직접 알려주어야 한다.
- 즉, 자기 메모리를 직접 관리하는 클래스라면 프로그래머는 항시 메모리 누수에 주의해야 한다!!!
[메모리 누수를 일으키는 주범들?]
- 캐시
- 리스너(listener) 혹은 콜백(callback)
- 콜백은 약한 참조로 저장하면 가비지 컬렉터가 즉시 수거해간다.
- WeakHashMap에 키로 저장하는 등의 방법이 있다.
[핵심]
- 메모리 누수는 겉으로 잘 드러나지 않아 시스템에 수년간 잠복하는 경우도 많다.
- 이런 누수는 철저한 코드 리뷰, 힙 프로파일러 같은 디버깅 도구를 동원해야만 발견되기도 하기에 이런 종유의 문제는 예방법을 익혀두는 것이 매우 중요하다.