일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- FLUTTER
- RxAndroid
- Android P
- Android
- android push
- 프로그래머스
- Python
- 알고리즘
- flutter firestore
- NDK
- Java
- 코틀린
- Kotlin
- Django REST framework
- RxJava
- C
- C++
- Flutter TextField
- Rxjava2
- dart
- android architecture component
- Django REST
- livedata
- 안드로이드 구글맵
- C/C++
- UWP
- kodility
- mfc
- 안드로이드
- Django REST Android
- Today
- Total
개발하는 두더지
[Effective Java 규칙6] 유효기간이 지난 객체 참조는 폐기해야 한다 본문
Effective Java 2/E 책과 구글링을 통해 내용을 정리하고 개인적인 견해가 포함된 글입니다.
C/C++ 처럼 손수 메모리를 관리해야하는 언어와 달리 자바는 GC에 의해 자동으로 적절한 시기에 안쓰는 메모리를 해제한다
하지만 자바도 메모리릭이 분명 발생하며 그 결과로 GC가 할일이 많아져 성능이 저하되고 메모리 요구량이 증가할 것이다
극단적으로 디스크 페이징이 발생하고 OOM(Out Of Memory)가 출력되며 프로그램이 중단될 것이다
public class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_SIZE = 4;
public Stack() {
elements = new Object[DEFAULT_SIZE];
}
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);
}
}
스택이 커졌다가 줄어들면서 제거한 객체들을 GC가 처리하지 못하므로 메모리릭이 발생한다.
1 |
2 |
3 |
4 |
5 |
6 |
|
|
이 상태에서 pop()을 호출하면 6이란 값이 반환되지만 Stack 클래스 내부에서는 객체를 해제한 것이 아닌 배열 포인터만 이동시켰다.
즉, element[5] 는 6이란 값이 들어있어서 참조상태이며 아직 사용안하고있는 상태( 클래스가 소멸되지 않는 한 언젠간 사용될 가능성이 높은 상태로 판단)이기 때문에 GC 수집에 포함되지 않는다.
프로그래머 입장에서는 0~4 인덱스까지가 실제 사용될 영역이고 5 인덱스는 프로그램 상 절대 사용될 일이 없는 값이다
하지만 GC는 그것을 알 수가 없다. 이렇게 실수로 객체 참조가 유지되는 경우 해당 객체만 GC에서 제외되는 것이 아닌 그 객체를 통해 참조되는 다른 객체들까지 모두 GC 수집에서 제외된다. 이게 많으면 많을 수록 성능에 큰 영향을 끼칠 것으로 보인다.
간단하게 pop된 객체는 null로 만드는 것이다.
public Object pop() {
if(size == 0)
throw new EmptyStackException();
Object results = elements[--size];
elements[size] = null;
return results;
}
캐시에서도 메모리 누수가 흔히 발생한다.
객체 참조를 캐시 안에 넣고 잃어버리는 일이 많은데 이를 해결하기 위해서 WeakHashMap을 사용하는 것이다. 즉, 캐시 바깥에서 키를 참조하고 있을 때만 값을 보관하면 될 때 쓸 수 있는 전략이다. 주의해야할 점은 WeakHashMap은 캐시 안에 보관되는 항목의 수명이 키에 대한 외부 참조의 수명에 따라 결정되는 상황에만 적용 가능하다는 것을 기억하자. (무슨말??, WeakHashMap과 이 내용을 찾아보자)
일반적으로 캐시에 보관되는 항목의 수명은 캐시에 보관된 기간에 따라 결정되는 것이 보통이다. 캐시를 정리하는 방법에는 LRU 기법이 있다.
인터페이스 콜백 리스너를 만들 때 리스너를 set해주고 명시적으로 해제하지 않으면 메모리에 점유된 상태로 유지된다.
Weak reference 약한 참조를하면 콜백을 즉시 처리하는 가장 좋은 방법이다
Weak reference는 reference count가 0이기 때문에 GC의 수집 대상이며 사용할 때 항상 null 체크를 해야한다
정리해보면,
자체적으로 관리하는 메모리가 있는 클래스를 만들 때 메모리 누수가 발생하지 않도록 주의해야 한다.
* 의견 1
다른 진영에서는 GC에서 하는 일을 신경쓰는 것 보다 코드에 집중해라. 이런 의견도 있음.
* 의견 2
안드로이드의 경우 라이프사이클이 짧고 객체를 생성하고 삭제하는 주기가 짧기때문에 자원을 그렇게 신경쓰지 않아도 되는게 아닌가?
주기가 짧아서 객체의 소멸이 발생하기때문에 GC의 대상에 자연스럽게 올라갈 것이므로 객체의 생성과 소멸에 크게 신경안써도 될 것 같다.
예를들면 rx와 같은 함수형 프로그래밍에서는 저자가 다시는 객체의 생성과 소멸에 신경을 쓰는 언어를 쓰고싶지 않다.라고 한다.
그리고 스터디하면서 자바가 객체의 생성과 소멸을 신경쓰면 C C++ 처럼 저수준 언어로 사용하는 것이며 자바 GC의 장점을 버리는 행위 같다
'Java,Android' 카테고리의 다른 글
Effective Java로 Java를 효과적으로 사용해보자 (0) | 2018.09.04 |
---|---|
[Effective Java 규칙7] 종료자 사용을 피해라 (0) | 2018.09.04 |
[Effective Java 규칙4] 불필요한 객체는 만들지 말자 (0) | 2018.09.04 |
[Effective Java 규칙3] private 생성자나 enum 자료형은 싱글턴 패턴으로 설계 (0) | 2018.09.04 |
[Effective Java 규칙2] 생성자 인자가 많을 때는 Builder 패턴을 이용 (0) | 2018.09.04 |