본문은 Effective Java를 읽고 정리한 내용을 기반으로 작성된 글입니다.
[ item 6. 불필요한 객체 생성을 피하라 ]
똑같은 기능의 객체를 매번 생성하기보다는 객체 하나를 재사용하는 편이 나을 때가 많다. 재사용은 빠르고 세련되다.
☑️ 기존의 인스턴스 재사용하기
String s = new String("bikini") // X
String s = "bikini" // O
☑️ 생성자 대신 정적 팩터리 메서드 사용하기
생성자 대신 정적 팩터리 메서드를 제공하는 불변 클래스(item1) → 불필요한 객체 생성을 피할 수 있음
↔ 생성자는 호출할 때마다 새로운 객체를 만들지만, 팩터리 메서드는 X
ex) Boolean(String) 대신 Boolean.valueOf(String) 사용하기
☑️ 무거운 객체 이슈
생성비용이 아주 비싼 객체가 반복해서 필요하다 → 캐싱하여 재사용하자
ex) String.matches()
- 이 메서드 내부에서는 정규표현식용 Pattern 인스턴스를 생성하고, 한 번 쓰고 버려져 바로 가비지 컬렉션 대상이 됨
- Pattern은 유한 상태 머신(finite state machine)을 만들기 때문에 인스턴스 생성 비용이 높음
→ 성능을 개선하려면 (불변)Pattern 인스턴스를 클래스 초기화(정적 초기화) 과정에서 직접 생성해 캐싱해두고, 호출될 때마다 재사용하자.
정적 초기화 : 객체의 인스턴스화 없이 클래스가 로드될 때 실행되는 초기화
☑️ 오토박싱(auto boxing) 이슈
오토박싱 : 기본 타입, 박싱된 기본 타입을 섞어 쓸 때 자동으로 상호 변환해주는 기술
private static long sum() {
Long sum = 0L;
for (long i=0; i<=Integer.MAX_VALUE; i++)
sum += i;
return sum;
}
- sum 변수를 long이 아닌 Long으로 선언 → 불필요한 Long 인스턴스가 2^31개나 생성됨
- 박싱된 기본 타입보다는 기본 타입을 사용하고, 의도치 않은 오토박싱이 숨어들지 않도록 주의하자
[ item 7. 다 쓴 객체 참조를 해제하라 ]
C, C++처럼 메모리를 직접 관리하는 언어를 쓰다가 자바로 넘어오면 메모리 관리에 더 이상 신경쓰지 않아도 된다고 오해할 수 있는데, 절대 사실이 아니다.
☑️ 메모리 누수
: 프로그램을 오래 실행하다 보면 점차 가비지 컬렉션 활동과 메모리 사용량이 늘어나 결국 서하가 저하되는 현상
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 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);
}
}
- 이 코드에서는 스택이 커졌다가 줄어들 때 스택에서 꺼내진 객체들을 가비지 컬렉터가 회수가지 않는다.
- 프로그램에서 그 객체를 더 이상 사용하지 않더라도 이 스택에 그 객체들의 참조를 여전히 갖고있기 때문
따라서 단 몇개의 객체가 매우 많은 객체를 회수하지 못하게 할 수 있음 → 잠재적으로 악영향
해법 : 해당 참조를 다 썼을 때 Null처리(참조 해제)를 하면 된다.
☑️ 그렇다면 모든 객체를 다 쓰자마자 null처리하면 되는걸까?
: 안된다.
- 객체 참조를 null처리하는 일은 예외적인 경우여야 한다.
- 가장 좋은 방법은 그 참조를 담은 변수를 유효 범위(scope) 밖으로 밀어내기
- 변수의 범위를 최소가 되게 정의하기(item57)
☑️ 그렇다면 null처리는 언제?
: 일반적으로 자기 메모리를 직접 관리하는 클래스라면 프로그래머는 항시 메모리 누수에 주의해야 한다.
- 원소를 다 사용한 즉시 그 원소가 참조한 객체들을 null처리 → 가비지 컬렉터에게 알리기
☑️ 메모리 누수를 일으키는 다른 주범들
- 캐시 역시 메모리 누수를 일으키는 주범이다.
- WeakHashMap을 만들자.
- 리스너(listener), 콜백(callback)또한 메모리 누수의 주범이다.
- 콜백을 약한 참조(weak reference)로 저장하자.