티스토리 뷰
728x90
반응형
다 쓴 객체 참조를 해제하라
- 자바에서는 가비지 컬렉터가 메모리를 알아서 관리 해주니까 프로그래머가 메모리 관리에 신경써야 하는 부분이 적습니다. 그래서 자칫 메모리 관리에 더 이상 신경쓰지 않아도 된다고 오해할 수 있는데, 절대 사실이 아닙니다.
- 일반적으로 객체 참조가 해제되서 객체를 참조하는 곳이 없게되면 가비지 컬렉터는 해당 객체를 회수해갑니다.
💡예제 코드
- 아래 예제 코드에서는 스택이 커졌다가 줄어들었을 때 스택에서 꺼낸 객체들을 가비지 컬렉터가 회수하지 않습니다. 프로그램에서
해당 객체를 더 이상 사용하지 않더라도 회수를 하지 않습니다. - 예제 코드에서는 스택이 배열에 다 쓴 참조를 여전히 가지고 있습니다.(다 쓴 참조란 더이상 쓰이지 않을 참조란 의미)
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];
}
private void ensureCapacity() {
if (elements.length == size) {
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
}
💡문제가 발생한 부분은?
- 아래 pop() 메서드를 호출할 때마다 size가 줄어들면서 객체배열 elements에서 size 인덱스에 위치한 값들을 반환하고 있습니다.
- 예를들어 elements라는 객체 배열의 크키가 5이고 [1, 2, 3, 4, 5] 라는 값이 들어 있을 때 pop() 메서드를 2회 호출해서 현재 size는 3일 것입니다. 여기서 pop() 메서드로 꺼낸 객체 4, 5는 어딘가에서 두둥실 떠 다닐것입니다.
- 궁금증. 다시 push() 메서드를 사용해서 다른 값을 밀어 넣으면 기존 값들은 가비지 컬렉터가 회수하는게 아니였던건가?
public Object pop() {
if (size == 0) {
throw new EmptyStackException();
}
return elements[--size];
}
💡해결 방안
- 해당 참조를 다 썻을 때 null처리를 해줍니다.
- 이렇게 다 쓴 참조를 null 처리하면 다시 사용하려 했을 때 NPE가 발생하기에 비정상적인 접근도 확인할 수 있습니다.
- 하지만 객체 참조를 null 처리하는 것은 예외적인 경우여야하고 더 나은 방법은 다 쓴 참조를 유효 범위 밖으로 밀어버리는 것입니다.
- 일반적으로 자기 메모리를 직접 관리하는 클래스라면 프로그래머는 항시 메모리 누수에 주의해야 합니다. 원소를 다 사용한 즉시 그
원소가 참조한 객체들을 다 null 처리해줘야 합니다.
public Object pop() {
if (size == 0) {
throw new EmptyStackException();
}
Object result = elements[--size];
elements[size] = null; // 다 쓴 참조 해제
return result;
}
💡Java의 Stack 클래스
- 자바의 스택은 자기 자신의 메모리를 직접 관리합니다. 그렇기 때문에 메모리 누수에 취약합니다.
- Stack 클래스도 내부적으로는 가비지 컬렉터가 일을 제대로 할 수 있도록 null로 처리를 하는 것을 알 수 있습니다.
public class Stack<E> extends Vector<E> {
public synchronized E pop() {
E obj;
int len = size();
obj = peek();
removeElementAt(len - 1);
return obj;
}
}
public class Vector<E> ...생략 {
public synchronized void removeElementAt(int index) {
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " +
elementCount);
}
else if (index < 0) {
throw new ArrayIndexOutOfBoundsException(index);
}
int j = elementCount - index - 1;
if (j > 0) {
System.arraycopy(elementData, index + 1, elementData, index, j);
}
modCount++;
elementCount--;
elementData[elementCount] = null; /* to let gc do its work */
}
}
메모리 누수의 원인과 해결책
다 쓴 객체의 참조를 해제하지 않기 때문에 메모리 누수가 발생하는데 어떤 경우가 있는지 살펴보자.
💡자기 메모리를 직접 관리하는 클래스에서 제대로 관리는 못하는 경우
- Java의 Stack 클래스처럼 자기 메모리를 직접 관리하는 경우 더 이상 참조가 되지 않는 객체들을 직접 참조 해제를 해주어야 합니다. 그렇지 않으면 예제처럼 가비지 컬렉터가 제대로 일을 수행못하게 됩니다.
- 유효범위 밖이거나 쓸 일이 없어지는 객체는 개발자가 신경써서 null 처리를 해주어야 합니다.
💡캐시
- 객체 참조를 캐시에 넣고 그 부분을 잊어버린다면 객체를 다 쓴 뒤에도 참조 해재게 되지 않기때문에 메모리에 쌓이게 됩니다.
💡리스터와 콜백
- 클라이언트에서 콜백을 등록만하고 해제를 하지 않는다면 콜백은 쌓여만 갑니다.
- WeahHashMap가은 콜렉션에 약한 참조로 저장하면 사용 후 가비지 컬렉터에 의해 수거됩니다.
참고자료)
https://catsbi.oopy.io/d7f3a636-b613-453b-91c7-655d71fda2b1
728x90
반응형
'스터디 > 이펙티브 자바' 카테고리의 다른 글
이펙티브 자바 - Item9. try-finally 보다는 try-with-resources를 사용하라. (0) | 2022.07.04 |
---|---|
이펙티브 자바 - Item8. finalizer와 cleaner 사용을 피하라. (0) | 2022.07.03 |
이펙티브 자바 - Item6. 불필요한 객체 생성을 피하라. (0) | 2022.07.02 |
이펙티브 자바 - Item5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라. (0) | 2022.07.02 |
이펙티브 자바 - Item4. 인스턴스화를 막으려거든 private 생성자를 사용하라. (0) | 2022.07.02 |
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
TAG
- transactional outbox pattern spring boot
- 람다 표현식
- spring boot redisson sorted set
- 레이어드 아키텍처란
- java userThread와 DaemonThread
- spring boot poi excel download
- spring boot 엑셀 다운로드
- spring boot redis 대기열 구현
- spring boot excel download paging
- spring boot redisson destributed lock
- spring boot redisson 분산락 구현
- 트랜잭셔널 아웃박스 패턴 스프링 부트 예제
- space based architecture
- redis 대기열 구현
- pipeline architecture
- spring boot excel download oom
- microkernel architecture
- 서비스 기반 아키텍처
- transactional outbox pattern
- 공간 기반 아키텍처
- pipe and filter architecture
- 자바 백엔드 개발자 추천 도서
- redis sorted set
- java ThreadLocal
- @ControllerAdvice
- service based architecture
- 트랜잭셔널 아웃박스 패턴 스프링부트
- polling publisher spring boot
- JDK Dynamic Proxy와 CGLIB의 차이
- redis sorted set으로 대기열 구현
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
글 보관함