이 포스팅에서 작성하는 내용은 EFFECTIVE JAVA(이펙티브자바) 에서 발췌하였습니다.
아이템 6. 불필요한 객체 생성을 피하라
- 똑같은 기능의 객체를 매번 생성하기보다는 객체 하나를 재사용하는 편이 빠르고 세련된 경우가 많다.
- 불변 객체(String)는 언제든 재사용할 수 있다. (불변 객체라면 재사용해도 안전함이 명백함)
- 생성자 대신 정적 팩터리 메서드를 제공하는 불변 클래스에서는 정적 팩터리 메서드를 사용해 불필요한 객체 생성 피할 수 있다.
→ Boolean(String) 생성자 대신, Boolean.valueOf(String) 팩터리 메서드를 사용하는 것이 좋음(생성자는 새로운 객체를 만들기 때문)
1. String
1) String 선언의 나쁜 예
- 실행될 때마다 새로운 String 인스턴스를 생성하기에 좋지 않은 코드
- 수백만 번을 반복하는 반복문 내에서 아래와 같은 코드가 사용된다면 인스턴스가 수백만 개가 생성됨.
String str = new String("hello World");
2) String 선언의 좋은 예
- 새로운 인스턴스를 만드는 대신에 하나의 String 인스턴스를 사용
- 나아가 이 방식을 사용하는 경우 같은 가상 머신 안에서 이와 똑같은 문자열 리터럴을 사용하는 모든 코드가 같은 객체를 재사용함이 보장
String str = "hello World";
2. 생성 비용이 비싼 객체의 경우에 재사용을 하면 더욱 효율적이다.
String.mathces(String s) : 주어진 문자열이 해당 정규표현식에 적합하는 지 알려주는 메서드
아래에서 주어진 문자열이 로마 숫자인지 확인하는 메서드를 예로 들어보겠다.
1) 일반적으로 사용하는 방법
- 이와 같은 방법으로 성능이 중요한 상황에서 반복해 사용하기엔 적절하지 않음
- String.mathces() 메서드 내부에서 사용하는 Pattern 인스턴스는 한 번 쓰고 버려짐 (GC의 대상이 됨)
- Pattern은 입력받은 정규표현식에 해당하는 유한 상태 머신을 만들기 때문에 인스턴스 생성 비용이 높음
static boolan isRomanNumeral(String str) {
return str.matches("^(?=)M*(C[MD]|D?C{0,3})" + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3}$");
}
2) Pattern을 미리 초기화해두고 재사용하는 방법 (성능 개선)
- Pattern 인스턴스를 매번 재생성하지 않고 미리 생성해서 재사용하기 때문에 성능이 훨씬 개선됨
public class RomanNumerals {
// String.matches() 메서드 내부에서 생성하는 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 isRomanNumeral(String str) {
// 위에서 생성해둔 Pattern 인스턴스를 재사용함
return ROMAN.matcher(str).matches();
}
}
3. 불필요한 오토박싱은 피하자.
박싱된 기본 타입보다는 기본 타입을 사용하고, 의도치 않은 오토박싱을 조심해야한다.
예시)
- 로직 상에는 문제가 없음. (정확한 값을 도출함)
- sum 변수를 long이 아닌 Long으로 선언하여 Long 인스턴스가 반복하면서 계속 생성됨 (long타입인 i가 Long 타입인 sum에 더해질 때마다 Long으로 오토박싱됨)
private static long sum() {
Long sum = 0;
for(long i=0; i <= Integer.MAX_VALUE; i++) {
sum += i;
}
return sum;
}
정리
- 요즘 JVM에서는 작은 객체 정도는 생성, 회수하는 일이 크게 부담되지 않는다.
- 오히려 프로그램의 명확성, 간결성, 기능을 위해서 객체를 추가로 생성하는 것은 일반적으로 좋다.
- 추가로, 아주 무거운 객체(DB 커넥션)이 아닌 경우에 단순히 객체 생성을 피하기 위해 객체 풀을 만드는 것은 코드를 헷갈리게 만들고 메모리 사용량을 늘려 성능을 떨어트린다.
- 무수히 반복하는 로직, 생성 비용이 비싼 객체의 경우에는 불필요한 객체 생성을 피하는 것을 고려해보자
참고)
이번에 공부한 주제는 추후에 공부하게 될 아이템 50과 대조적인 주제이다.
아이템 50 : "새로운 객체를 만들어야 한다면 기존 객체를 재사용하지 마라"
방어적 복사가 필요한 상황에서 객체를 재사용했을 때의 피해가 필요없는 객체를 반복 생성했을 때의 피해보다 훨씬 크다.
'BE > Java' 카테고리의 다른 글
[Effective Java] 아이템 8. finalizer와 cleaner 사용을 피하라 (0) | 2022.09.06 |
---|---|
[Effective Java] 아이템 7. 다 쓴 객체 참조를 해제하라 (0) | 2022.09.05 |
[Effective Java] 아이템 5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (0) | 2022.09.01 |
[Effective Java] 아이템 4. 인스턴스화를 막으려거든 private 생성자를 사용하라 (0) | 2022.08.31 |
[Effective Java] 아이템 3. private 생성자나 열거 타입으로 싱글턴임을 보장하라. (0) | 2022.08.31 |