-
아이템7. 다 쓴 객체 참조를 해제하라책/이펙티브자바 2021. 6. 23. 22:53
Java의 경우 GC가 메모리 관리를 해준다.
그래서 메모리 관리에 더 이상 신경을 쓰지 않아도 된다고 오해할 수 있으나 절대 사실이 아니다.
// 일반적인 Stack 의 pop 메소드 public Object pop() { if (size == 0) { throw new EmptyStackException(); } return elements[--size]; }
특별한 문제는 없어 보일 수 있다. 하지만 '메모리 누수' 의 문제가 있는데 이 프로그램을 오래 실행하면 점차 GC 활동과 메모리 사용량이 늘어나 결국 성능이 저하될 것이다.
상대적으로 드문 경우긴 하지만 디스크 페이징이나 OutOfMemoryError를 일으킬 수 있다.
여기서 문제점은 다 쓴 참조(obsolete reference)를 여전히 가지고 있다는 점이다.
프로그램에서 그 객체들을 더 이상 사용하지 않더라도 말이다.
해결방안은 해당 참조를 다 쓰면 null(참조 해제)로 초기화 해주면 된다.
// null 처리한 pop 메소드 public Object pop() { if (size == 0) { throw new EmptyStackException(); } Object result = elements[--size]; elements[size] == null; // 다 쓴 참조 해제 return result; }
다 쓴 참조를 null 처리하는데 이에는 다른 이점도 따라온다.
만약 null 처리한 참조를 실수로 사용하려 하면 프로그램이 즉시 NullPointerException 을 던지며 종료한다.
그렇다면 모든 객체를 null로 해야할까 ?
모든 객체를 다 쓰자마자 일일이 null 처리하는 경우도 있는데 그럴 필요도 없고 바람직하지도 않다.
객체 참조를 null 처리하는 일은 예외적인 경우여야 한다.
다 쓴 참조를 해제하는 가장 좋은 방법은 그 참조를 담은 변수를 유효 범위 밖으로 밀어내는 것이다.
메모리 누수
Stack 과 같이 자기 메모리를 직접 관리하는 클래스
Stack의 경우 자기 메모리를 직접 관리하는데 활성 영역과 비활성 영역 중 어느 부분이 사용되고 안되는지 GC의 입장에서는 알 수 가 없기에 메모리 누수가 발생할 수 있다.
해결방안
- 프로그래머나 아는 사실이므로 비활성 영역이 되는 순간 null 처리해서 해당 객체를 더 이상 쓰지 않을것임을 GC에 알려줘야한다.
캐시
객체 참조를 캐시에 넣고 다 쓴 이후에도 그냥 두는 경우가 있다.
해결방안
- 캐시 외부에서 Key를 참조하는 동안만 엔트리가 살아있는 캐시가 필요한 경우는 WeakHashMap 을 사용한다.
- 엔트리의 유효기간을 정해둔다.
- 사실상 계산하는것이 어렵다
- 쓰지 않는 엔트리를 청소한다.
- ScheduledThreadPoolExecutor와 같은 백그라운드 스레드를 활용하거나 캐시에 새 엔트리를 추가할때 부수 작업으로 수행하는 방법을 이용한다.
- LinkedHashMap의 경우 removeEldestEntry 메서드를 사용해 후자의 방식으로 처리한다.
- 더 복잡한 캐시를 만들고 싶을때는 java.lang.ref 패키지를 직접 활용한다.
콜백
- 콜백이란 이벤트가 발생하면 특정 메소드를 호출해 알려주는 것입니다.(1개)
- 리스너는 이벤트가 발생하면 연결된 리스너(핸들러)들에게 이벤트를 전달합니다.(n개)
- 클라이언트가 콜백을 등록만 하고 해지하지 않는다면 콜백은 쌓이게 될 것이다.
- 이럴 때 콜백을 약한 참조(weak reference)로 저장하면 GC가 즉시 수거해간다.
- 예를 들어 WeakHashMap에 키로 저장해두면 된다.
결론
- 메모리 누수를 방지하는 방법은 다쓴 객체 참조를 null로 처리하는 것과 지역변수의 범위를 최소화 하는 방법이다.
- 모든 것을 null로 처리한다고 해서 좋은 것은 아니다. 가장 좋은 방법은 지역 변수의 범위를 최소화 하는 방법이다.
- 메모리 누수의 주범은 자기 메모리를 직접 관리하는 경우, 캐시, 리스너, 콜백이다.
- 자기 메모리를 직접 관리하는 클래스라면 프로그래머는 항시 메모리 누수에 주의해야 한다.
- 메모리 누수는 철저한 코드리뷰, 힙 프로파일링 도구를 통해 디버깅을 해야 발견할 수 있기 때문에 메모리 누수를 철저히 신경써야 한다.
'책 > 이펙티브자바' 카테고리의 다른 글
아이템9. try-finally보다는 try-with-resources를 사용하라 (0) 2021.06.26 아이템8. finalizer와 cleaner 사용을 피하라 (0) 2021.06.26 아이템6. 불필요한 객체 생성을 피하라 (0) 2021.06.23 아이템5. 자원을 직접 명시하지말고 의존 객체 주입을 사용하라 (0) 2021.06.21 아이템4. 인스턴스화를 막으려거든 private 생성자를 사용하라 (0) 2021.06.20