불필요한 객체가 반복적으로 생성되고 있는 것을 피해야한다.
불필요하게 객체가 반복적으로 생성하지 않도록 하기 위하여 Flyweight 패턴을 사용하여 같은 객체를 공유해서 사용하거나, 생성 비용이 비싼 객체를 캐싱하여 재사용해야한다.
불필요한 객체를 생성하는 가장 간단한 예로 String s = new String("example")
이 있다. 실행될 때마다 String 인스턴스를 새로 만든다.
하지만 String s = "example"
의 경우 새로운 인스턴스를 매번 만드는 것이 아니라 가상 머신 상수 풀에서 "example" 하나를 공유해서 사용한다.
캐싱의 일환으로 정적 필드에 중복해서 사용하는 인스턴스를 초기화하여 반복 사용하는 것으로 반복 생성으로 인한 성능 하락을 해소할 수 있다.
정적 초기화에서 지연 초기화(Lazy Initialization) 이야기가 나오는데, 지연 초기화는 코드를 복잡하게 만드는데, 성능은 크게 개선되지 않을 때가 많기에 권하지 않는다고 한다.
불필요한 객체를 만들어내는 또 다른 예로 오토박싱이 있다. 기본 타입을 오토박싱하는 과정에서 인스턴스가 생성되는데, 의도치 않게 수많은 불필요한 객체를 생성할 여지가 있다.
private static long sum(){
Long sum = 0L;
for( long i = 0; i <= Integer.MAX_VALUE; i++){
sum += i;
}
return sum;
}
sum += i
에서 long을 Long 타입으로 오토박싱하는데 Long 인스턴스가 약 21억개 만들어진다. 박싱된 기본 타입보다는 기본 타입을 사용하고, 의도치 않은 오토박싱이 숨어들지 않도록 주의하자.
아주 무거운 객체가 아닌 다음에야 단순히 객체를 생성을 피하고자 커스텀 객체 풀을 만들지 말자.
JVM GC가 잘 되어있으니 굳이 만들어서 코드를 어렵게 하고, 메모리를 오히려 더 쓰며, 성능을 낮출 필요가 없다.
기존 객체를 재사용해야 한다면 새로운 객체를 만들지 마라
하지만 새로운 객체를 만들어야 한다면 기존 객체를 재사용하지 마라
방어적 복사가 필요한 상황에서 객체를 재사용했을 때의 피해가 필요 없는 객체를 반복 생성했을 때의 피해보다 훨씬 크다. 방어적 복사에 실패하면 언제 터져 나올지 모르는 버그와 보안 구멍으로 이어지지만, 불필요한 객체 생성은 그저 코드 형태와 성능에만 영향을 미친다.