[Effective Java] item6 불필요한 객체 생성을 피하라
String s = new String("bikini");
=> 실행될 때마다 String 인스턴스를 새로 만든다.
이 문장은 반복문이나 비번하게 호출할 경우 수백만개의 String 인스턴스가 생성될 수 있는 코드이다.
String s = "bikini";
로 코드를 수정함으로서 같은 객체를 재사용하도록 개선할 수 있다.
불필요한 객체 생성을 피할 수 있는 방법은 여러개가 있다.
1) 정적 팩터리 메서드를 사용해서 불필요한 객체 생성을 피할 수 있음
ex) Boolean(String) 생성자 대신에 Boolean.valueof(String) 팩터리 메서드를 사용
= 생성자는 호출때마다 새로운 객체를 생성하지만, 팩터리 메서드는 그렇지 않다.
2) 캐싱을 통해 재사용할 수 있음
static boolean isRomanNumeral(String s){
return s.matches("^(?=.)M*(C[MD]|D?C{0,3})" +
"(X[CL]|L?X{0,3})(I[XY]|V?I{0,3})$");
}
String.matches 메서드는 정규표현식으로 문자열 형태를 확인하는 가장 쉬운 방법이지만 반복해서 사용하기엔 적합하지 않다.
왜냐하면 메서드 내부에 정의하는 pattern인스턴스가 매번 새로 생성되고선 바로 가비지 컬렉션의 대상이 되기 때문이다.
private static final Pattern ROMAN = Pattern.compile("^(?=.)M*(C[MD]|D?C{0,3})" +
"(X[CL]|L?X{0,3})(I[XY]|V?I{0,3})$");
static boolean isRomanNumeral(String s){
return ROMAN.matcher(s).matches();
}
Pattern 인스턴스를 초기화 과정에서 캐싱해두고 isRomaNumeral 메서드를 호출할 때마다 이 인스턴스를 재사용하도록 개선할 수 있음
※ 지연 초기화 : 처음 호출될 때 초기화 진행 (코드가 복잡해지고 성능적으로 크게 개선되지 X)
[주의사항]
불변 객체라면 재사용해도 안전함을 보장하지만 가변의 경우 주의해서 사용해야 된다.
static long sum(){
Long sum = 0L;
for (long i = 0; i < Integer.MAX_VALUE; i++) {
sum += i;
}
return sum;
}
위 코드를 보면 큰 수를 표현하기 위해 Long 타입으로 sum을 담고 있다.
근데 long 타입을 i를 오토박싱을 통해 Long 타입으로 변환되면서 더해지고 있다. 즉 Long으로 선언해서 불필요한 Long 인스턴스가 대량으로 생성되는 것이다.
※ 오토박싱 : 기본 타입과 박싱된 기본 타입을 섞어 쓸때 자동으로 상호 변환해주는 기술 (기본타입을 완전히 없애주는 것 X)
하지만, 무조건 객체 생성은 비싸니 피해야 된다는 의미는 아니다.
작은 객체를 생성하고 회수는 하는 정도는 성능적으로 크게 이슈가 있지 않기 때문이다.
데이터베이스 연결같이 생성 비용이 큰 경우에만 객체 풀을 통해 불필요한 객체 생성을 피하는 것이 좋다.
즉, 필요한 상황에서 객체를 재사용했을때의 피해가 필요없는 객체를 반복 생성했을때의 피해보다 훨씬 크다는걸 잊으면 안된다.