[Effective Java] 아이템6 - 불필요한 객체 생성을 피하라
Note: 이 글은 Effective Java 책을 읽으면서 중요한 내용 및 알아야 하는 지식들을 정리한 글입니다.
불변 객체, 또는 사용 중에 변경되지 않는 가변 객체는 매번 생성하기 보다, 재사용 하는 것이 바람직하다.
불필요한 객체 생성 - String
String s = new String("mongsil"); // Never follow!
“mongsil” 자체가 String 객체인데, 이를 다시 new String()
으로 감싸서 String 인스턴스를 다시 만들었다.
[아이템1] 에서 공부한 정적 팩터리 메서드도 불필요한 객체 생성을 피한 케이스로 볼 수 있다.
// Boolean(String) deprecated in Java9
Boolean.valueOf(String) // static factory method
비싼객체 반복생성 - String matches
생성하기 다소 무거운 객체 (expensive Object) 라면 캐싱해서 재사용할 수 있다. 다음은 정규표현식을 활용해서 주어진 문자열이 유효한 로마 숫자인지를 확인하는 메서드이다.
static boolean isRomanNumeralSlow(String s) {
return s.matches("^(?=.)M*(C[MD]|D?C{0,3})"
+ "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
}
String.matches()
메서드의 내부 로직을 보면 Pattern.matches
를 호출하게 된다. 여기서 Pattern
인스턴스를 매번 생성하는 것을 확인할 수 있다.
// Pattern.matches
public static boolean matches(String regex, CharSequence input) {
Pattern p = Pattern.compile(regex); // (1)
Matcher m = p.matcher(input);
return m.matches();
}
public static Pattern compile(String regex) {
return new Pattern(regex, 0); // (2)
}
Pattern.matches
메소드 위에 친절하게 아래와 같이 가이드가 나와 있다.
If a pattern is to be used multiple times, compiling it once and reusing it will be more efficient than invoking this method each time.
성능을 개선하려면 Pattern 인스턴스를 클래스 초기화 과정에서 생성해 캐싱해두고, 재사용하는 방법이 있다.
private static final Pattern ROMAN = Pattern.compile(
"^(?=.)M*(C[MD]|D?C{0,3})"
+ "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
static boolean isRomanNumeralFast(String s) {
return ROMAN.matcher(s).matches();
}
어떤 패턴인지 이름을 주어 훨씬 명확해졌으며, 객체를 재사용할 수 있게 되었다.
성능이 Dramatic 하게 빨라진다.. 6배는 빨라지는 것 같다. 정말 중요하군..
오토박싱 (auto boxing)
오토박싱은 프로그래머가 기본 타입과(long) 박싱된 기본 타입(Long)을 섞어 쓸 때 자동으로 상호 변환해주는 기술이다.
private static long sum() {
Long sum = 0L;
for (long i = 0; i <= Integer.MAX_VALUE; i++)
sum += i;
return sum;
}
반복문에서 long
타입인 i
를 Long
타입인 sum
에 더할 때마다 불필요한 Long 인스턴스가 생성되고 자동으로 형변환이 된다.
여기서 sum 의 타입을 long
으로만 바꿔줘도 성능이 훨씬 빨라진다.
Point : 박싱된 기본 타입보다 기본타입을 사용하고, 의도치 않는 Auto Boxing이 숨어들지 않도록 주의하자.
댓글남기기