Item6 - 불필요한 객체 생성을 피하라

2023. 11. 17. 22:55Book/이펙티브 자바

 - 정규식, Pattern => 생성 비용이 바싼 객체라서 반복해서 생성하기 보다, 캐싱하여 재사용하는 것이 좋다.

 - auto boxing       => 기본 타입과 박싱된 기본 타입을 섞어서 사용하면 변환하는 과정에서 불필요한 객체가 생성될 수 있다.

 

 

똑같은 기능의 객체를 매번 생성하기보다는 객체 하나를 재사용하는 편이 나을 때가 많다. 재상용은 빠르고 세련되다. 특히 불변 객체는 언제든 재사용할 수 있다.

다음 코드는 하지 말아야 할 극단적인 예이다.

String s1 = new String("bikini");

String s2 = "bikini";
Stirng s3 = "bikini";

 

s1 변수는 할당될 때마다 String 인스턴스를 새로 만든다. 완전히 쓸데 없다.

s2와 s3는 jvm 내에서 이와 똑같은 문자열 리터럴을 사용하는 모든 코드가 힙 영역의 같은 객체를 가리킨다

 

 

생성자 대신 정적 팩터리 메서드를 제공하는 불변 클래스에서는 정적 팩터리 메서드를 사용해 불필요한 객체 생성을 피할 수 있다. => Boolean.valueOf(String)

 

 

생성 비용이 아주 비싼 객체도 더러 있다. 이런 비싼 객체는 반복해서 필요하다면 캐싱하여 재사용 하는걸 권장한다.

1회용
static boolean isRomanNumeral(String s) {
	return s.matchers("^(?=.)M*(C[MD]|D?{0,3}")
    		+ "(X[CL]|L?X{,3})(I[xv]|V?I{0,3})$");
}

===========================================================
재사용
public class RomanNumerals {
	private static final Pattern ROMAN = Pattern.compile(
    	"^(?=.)M*(C[MD]|D?{0,3}")
    	+ "(X[CL]|L?X{,3})(I[xv]|V?I{0,3})$");
	
    static boolean isRomanNumerals (String s) {
    	return Roman.matcher(s).matches();
    }
}

 

String.matches는 내부에서 Pattern 데이터 타입의 비싼 인스턴스를 한번 쓰고 버려져 가비지 컬렉션 대상이 된다.

비싼 객체라면 재사용을 통해 성능을 개선시키는 것이 좋다.

 

개선된 isRomanNumeral 방식의 클래스가 초기화 된 후 이 메서드를 한 번도 호출하지 않는다면 ROMAN 필드는 쓸데없이 초기화된 꼴이다.
ROMAN 필드를 메서드가 호출될 때 초기화 하는 지연 초기화를 할 수는 있지만 권하지는 않는다. 지연 초기화는 코드를 복잡하게 할 뿐만 아니라 성능은 크게 개선되지 않을 때가 많다.

 

불필요한 객체를 만들어내는 또 다른 예로 오토박싱을 들 수 있다. 박싱된 고본 타입보다는 기본 타입을 사용하고, 의도치 않은 오토박싱이 숨어들지 않도록 조심하자.

 

 

이번 아이템을 "객체 생성은 비싸지 피해야 한다"로 오해하면 안된다. JVM은 별다른 일을 하지 않는 작은 객체를 생성하고 회수하는 일이 크게 부담되지 않는다. 명확성, 간결성, 기능을 위해 객체를 생성하는 것이라면 일반적으로 좋다.

 

거꾸로 아주 무거운 객체가 아닌 다음에야 단순히 객체 생성을 피하고자 객체 풀을 만들지는 말자. 굉장히 무거운 객체가 아닌 이상 일반적으로 자체 객체 풀은 코드를 헷갈리게 하고 메모리 사용량도 늘리고 성능도 떨어뜨린다.