불필요한 객체 생성을 피하라
똑같은 기능의 객체를 매번 생성하기보다는 객체 하나를 재사용하는 것을 고려한다.
불변 객체는 언제든 재사용 가능
- String Literal
🤮String s= new String("abc"); //Heap 영역에 새로 생성
String s= "abc"; //String Constant Pool 에서 재사용
리터럴로 선언된 문자열을 사용함으로서 쓸데없는 문자열 인스턴스 생성을 방지한다.
- 정적 팩토리 메서드
🤮Boolean b = new Boolean("false"); //자바 9에서 deprecated API 지정
Boolean b = Boolean.valueOf("true");
//정적 팩토리 메서드
public static Boolean valueOf(String s) {
return parseBoolean(s) ? TRUE : FALSE;
}
//미리 생성된 객체
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
Boolean은 valueOf 메서드를 통해 미리 생성된 객체를 재활용한다.
- 캐싱
static boolean isRomanNumeral(String s) {
return s.matches("정규표현식");
}//String.matches 메서드 사용의 반복이 성능에 영향을 줄 수 있다.
public boolean matches(String regex) {
return Pattern.matches(regex, this);
}
public static boolean matches(String regex, CharSequence input) {
🤮Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(input);
return m.matches();
}
matches 내부에서 생성되는 Pattern 인스턴스(불변)는 한 번 쓰고 버려져, GC 대상이 된다.
Pattern은 정규표현식에 해당하는 유한 상태 머신을 만들기 때문에 인스턴스 생성 비용이 높다
public class RomanNumerals {
private static final Pattern ROMAN = Pattern.compile("정규표현식");
static boolean isRomanNumeral(String s) {
return ROMAN.matcher(s).matches();
}
}
정규표현식을 표현하는 Pattern 인스턴스를 정적초기화 과정에서 캐싱해두고, 나중에 문자열 형태를 확인하는 메서드가 호출 될 때마다 이 인스턴스를 재사용한다.
객체 하나당 어댑터 하나씩만 만들어지면 충분하다.
Map<Integer, String> map = new HashMap<>();
map.put(1,"a");
map.put(2,"b");
Set<Integer> keys1=map.keySet();
🤮Set<Integer> keys2=map.keySet();
keySet을 호출 할 때마다 새로운 Set 인스턴스가 만들어지는 것이 아님.
모두가 똑같은 Map 인스턴스를 대변하기 떄문에 여러개 만들 필요가 없다.
private static long sum() {
🤮Long sum = OL;
for (long i= 0; i <= Integer.MAX_VALUE; i++){
sum += i; //불필요한 Long 인스턴스 생성
}
return sum; }
의도치 않은 오토박싱이 들어가지 않도록 주의
- 객체 생성은 비싸니 피해야한다는 의미가 아님. 요즘 JVM에서 작은 객체를 생성하고 회수하는 일은 큰 부담이 아니다.
- 아주 무거운 객체가 아니라면, 객체 생성을 피하기 위해 객체 풀을 만들지 말자.
일반적으로 코드를 헷갈리게 만들고 메모리 사용량을 늘림. - 방어적 복사가 필요한 상황에서 객체를 재사용했을 때의 피해 >> 불필요한 객체를 반복 생성했을 때의 피해