자바가 가비지 컬렉터를 갖춘 언어라고 해서 메모리 관리는 무시해서는 안된다.
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 o){
ensureCapacity();
elements[size++] = o;
}
public Object pop(){
if (size == 0)
throw new EmptyStackException();
return elements[--size];
}
/*
원소를 위한 공간을 적어도 1개 이상 확보, 배열 크기를 늘려야 할 때마다 대략 두 배씩 늘린다.
*/
private void ensureCapacity(){
if (elements.length == size){
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
}
이 코드에서 메모리 누수가 발생하는 부분은 어디일까 ??
답은 pop() 메소드이다.
스택에서 꺼낸 객체들을 가비지 컬랙터가 회수하지 않기 때문이다.
(객체 참조 하나를 살려두면 가비지 컬렉터는 그 객체가 참조하는 모든 객체를 회수해가지 못함)
메모리 누수를 막기 위한 해결책으론 해당 참조 객체를 다 사용한 경우 null 처리를 해주면 된다.
public Object pop(){
if (size == 0)
throw new EmptyStackException();
Object o = elements[--size];
elements[size] = null;
return o;
}
하지만 null 처리에 집착할 경우 코드가 지저분해질 수 있다.
가장 좋은 방법은 참조를 담는 변수를 유효 범위 밖으로 밀어내는 것이다.
public static void main(String[] args) {
try{
stack s = new stack();
s.push("temp1");
s.push("temp2");
s.push("temp3");
Object a = s.pop();
Object b = s.pop();
}catch (Exception e){
System.out.println(e.getMessage());
}
}
스택의 경우 왜 메모리 관리에 취약한다.
스택은 자기 메모리를 직접 관리하기 때문이다.
스택은 배열의 활성화 영역에 속한 객체만을 사용하고 비활성화 영역은 사용하지 않는데 가비지 컬렉터는 이 사실을 알 수가 없다.
그러므로 null 처리를 통해 가비지 컬렉터에게 이 객체를 사용하지 않는다는 걸 알려야 한다.
자기 메모리를 직접 관리하는 클래스라면 프로그래머는 항시 메모리 누수에 주의해야 한다.
캐시 역시 자기 메모리를 직접 사용하기에 메모리 누수가 발생할 수 있다.
이 경우 만약 키를 참조하는 동안에만 살아있으면 되는 캐시라면 WeakHashMap을 사용하는 걸 권장한다.
WeakHashMap = 약한 참조 해쉬맵
"내가 D라는 데이터를 사용하는 동안 누가 D를 필요로 한다면 알려줘 같이 쓰자.
하지만 내가 필요없다면 그건 버리고 다음에 필요할 때 다시 생성할꺼야"
이 경우 메모리 누수를 막는 방법은 두가지가 있다.
1) ScheduledThreadPoolEcecutor와 같이 백그라운드 스레드 사용
2) 캐시에 새 엔트리를 추가할 때 부수 작업으로 수행하는 방법
ex) LinkedHashMap 인 경우 removeEldestEntry 메서드를 써서 null 처리를 할 수 있다.
리스너(=콜백) 역시 메모리 누수가 발생할 수 있다.
클라이언트가 콜백을 등록만 하고 명확히 해지하지 않는다면 콜백이 계속 쌓일 수 있기 때문이다.
해결 방법으로 콜백을 약한 참조에 저장하는 것이다.
예를 들어 weakHashMap에 키를 저장하면 된다.
[WeakHashMap 참고 블로그 링크]
'IT > 자바' 카테고리의 다른 글
[Effective Java] item 10 equals는 일반 규약을 지켜 재정의하라 (0) | 2021.07.25 |
---|---|
[Effective Java] item 8, 9 finalizer & cleaner 사용을 피해라 / try-finally 보다는 try-with-resources를 사용해라 (0) | 2021.07.25 |
[Effective Java] item6 불필요한 객체 생성을 피하라 (0) | 2021.07.25 |
[Effective Java] Item 5 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (0) | 2021.07.11 |
[Effective Java] Item 4 인스턴스화를 막으려거든 private 생성자를 사용하라 (0) | 2021.07.11 |