item6) 불필요한 객체 생성 지양

불필요한 객체 지양

  • 똑같은 기능의 객체를 매번 생성하기보다는 객체 하나를 재사용할 수 있도록 한다.

  • 불변 객체는 언제든 재사용 가능하다.

정적 팩토리 메서드 사용

  • 매번 새로운 객체를 만드는 생성자와 달리 불필요한 객체 생성을 피할 수 있다.

  • 불변 객체뿐만 아니라 가변 객체도 사용 중 변경이 없다 판단되면 재사용이 가능하다.

생성 비용이 비싼 경우 객체 캐싱

  • 아래와 같이 직접 matches를 사용할 경우 매번 Pattern 인스턴스가 새로 생성된다.

s.matches(
	"^(?=.)M*(C[MD]|D?C{0,3})"
	+"(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
  • 아래와 같이 Pattern final 객체를 선언해두면 반복해서 match()할 때 인스턴스를 재사용하도록 캐싱할 수 있다.

  • 코드의 의미도 훨씬 잘 드러난다.

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 s) {
	return ROMAN.matcher(s).match();
}

Pattern 객체는 입력받은 정규표현식에 대한 유한 상태 머신을 만들기 때문에 인스턴스 생성 비용이 높다.

어댑터(View)

어댑터

  • 실제 작업은 뒷단 객체에 위임하고 제2의 인터페이스 역할을 해주는 객체

  • 뒷단 객체만 관리하면 되므로 뒷단 객체 하나 당 하나만 만들어지면 충분하다.

  • 불변 객체를 사용해 재사용을 하는 것이 일반적이지만, 덜 명확할 수도 있고, 이러한 직관에 반대되는 상황이 있을 수도 있다.

  • 예시

    • Map.keySet()은 Map의 key를 모두 담은 Set 뷰 인스턴스를 반환한다.

    • Set 인스턴스는 가변 객체이지만 뒷단 객체 하나 당 하나만 만들어지면 충분하다.

    • 동일한 Map에서 keySet() 메서드를 여러번 호출 시 같은 인스턴스를 반환하여, 반환한 객체 중 하나를 수정하면 다른 모든 객체가 따라서 바뀐다.

오토박싱

  • 프로그래머가 기본 타입(int, long, ...)과 박싱된 기본 타입(Integer, Long, ...)을 혼용할 때 자동으로 상호 변환해주는 기술

  • 기본 타입과, 그에 대응되는 박싱된 기본 타입의 구분을 흐려주지만 완전히 없애지는 않는다.

  • 박싱된 기본타입을 사용할 경우 매번 새로운 인스턴스가 생성되기 때문에 성능이 저하될 수 있다.

  • 박싱된 기본 타입보다는 기본 타입을 사용하고, 의도치 않은 오토박싱이 발생하지 않도록 주의할 것

객체 풀 구현

  • 객체 생성을 피하고자 직접 객체 풀을 구현하는 방법이 있다.

  • 일반적으로 코드를 헷갈리게 만들고, 메모리 사용량을 늘리고, 성능을 떨어뜨린다.

  • JVM의 Garbage Collector는 잘 최적화되어 가벼운 객체를 다룰 때에는 객체 풀보다 빠르다.

재사용 vs 방어적 복사

  • 불필요한 객체 생성했을 때의 피해보다 (item50) 방어적 복사가 필요한 상황에서 객체를 재사용했을 때의 피해가 훨씬 크다.

  • 불필요한 객체 생성은 코드 형태와 성능에만 영향을 주지만, 방어적 복사에 실패하면 버그와 보안 피해가 언제 발생할 지 모른다.

Last updated