자바를 사용할 때 "GC"에 의존하여 메모리 자원 관리에 소홀해서는 안된다. GC 의 손이 닿지 않는 자원이 생기지 않도록 주의해야한다.
GC 관련 포스팅은> https://javabom.tistory.com/7
Memory Leak 예시
다 쓴 참조(obselete reference)
더이상 사용하지 않지만 참조되어있어 수거되지 않음. 뿐만 아니라 그 객체가 참조하고 있는 또 다른 객체까지 수거되지 않기 때문에 큰 문제가 발생할 수 있음.
메모리 누수 케이스
1. 자기 자신의 자원을 관리하는 클래스
이펙티브 자바 책에서는 Stack 을 예시로 든다. Stack이 자원을 pop 하기만 한다고 GC 대상이 되지는 않는다. 일반적으로 구현하는 pop 의 로직에는 stack size 를 -1 해줄 뿐이기 때문이다.
해결방법: null 처리
주의점: 무분별한 Null 처리는 코드를 지저분하고 복잡하게 만들 수 있다.
2. 캐시
캐시에 담아놓기만 하고 정리를 해주지 않는다면? 빠른 속도를 위해 사용하는 캐시에 자원이 계속 쌓이게 되면 결국 이 캐시는 캐시의 역할을 하지 못하게 된다.
해결방법:
- WeakHashMap
캐시에 넣어놓고 더이상 쓰지 않아도 계속 들어있을 수 있다. 일반적인 HashMap 은 사용여부에 관계없이 Key - Value 쌍을 지우지 않는다. WeakHashMap 은 특정 key 값이 더이상 사용되지 않는다고 판단되면 해당 Key - Value 쌍을 삭제해준다.
- 캐시의 경우 시간이 지남에 따라 사용되지 않으면 그 가치를 떨어트리는 방법을 사용한다.
(LinkedHashMap.removeEldestEntry -> 사용된지 가장 오래된 엔트리 삭제)
3. 리스너, 콜백
리스너와 콜백을 등록만하고 해지를 안하면 메모리 낭비.
해결방법: 약한 참조로 넣어서 가비지컬렉터의 수거 대상이 되도록 하자.
콜백 메서드 예시)
query 를 받아 실행하여 ResultSet 을 반환하는 인터페이스가 있다. 이 인터페이스는 "콜백" 메서드에 의해 작동하며 이 콜백 메서드를 정의하는 방법에 따라 GC 대상의 여부가 결정된다.
먼저, 필드에 콜백을 할당하는 방법이다.
콜백 인터페이스는 Connection 이다. Connection 인터페이스는 클라이언트 측에서 정의되며 이 CustomDatabaseManager 에 할당된다.
클라이언트는 Connection 을 정의하여 CustomDatabaseManager의 생성자 매개변수로 전달해주었다. 그리고 이 콜백 메서드는 한번 사용되면 다신 필요 없다고 가정하자.
단순하게 생각하면 connection 에 null 을 할당할 수 있을 것이다. 그 뒤 GC 가 수거해가기를 기다려보자.(gc 가 동작할거라고 100퍼센트 확신은 못하겠지만) 이 테스트는 아무리 여러번 돌려봐도 성공이다. 즉, databaseManager 의 connection 은 다신 사용되지 않아도 gc 의 대상이 되지 못한다.
디버깅을 걸어보면 클라이언트에서 할당한 null 은 databaseManager 에 전혀 영향을 주지 않는다는 것을 볼 수 있다.
이 경우에 Memeory leak 을 방지하는 방법은
databaseManager 에게 직접 connection에 null 을 할당하라고 명령하는 것이다.
또 다른 방식으로 콜백을 등록할 수 있다. 위에서 잠시 언급된 WeakHashMap 에 저장하는 것이다.
WeakHashMap 을 사용하면 클라이언트에서 null 을 할당해도 gc 의 대상이 되도록 할 수 있다.
'Reading Record > 이펙티브자바' 카테고리의 다른 글
[아이템 15] 클래스와 멤버의 접근 권한을 최소화하라 (0) | 2020.02.12 |
---|---|
아이템[13] - clone 재정의는 주의해서 진행하라 (0) | 2020.02.09 |
아이템 [3] - private 생성자나 열거타입으로 싱글턴임을 보증하라 (0) | 2020.02.08 |
아이템[14] - Comparable을 구현할지 고려하라 (0) | 2020.02.07 |
아이템[11] - equals를 재정의 하려거든 hashCode도 재정의하라 (2) | 2020.01.26 |