본문 바로가기
Books/Effective-Java 3판

6.불필요한 객체 생성을 피하라

by 두두리안 2021. 9. 23.
728x90
똑같은 기능의 객체를 매번 생성하기보다는 객체 하나를 재사용하는 편이 나을 때가 많다

String s = new String("bikini"); // 따라 하지 말것!

- 위 문장은 실행될 때마다 String 인스턴스를 새로 만든다.


String s = "bikini";

- 새로운 인스턴스를 매번 만드는 대신 하나의 String 인스턴스를 사용한다

- 문자열 리터럴을 사용하는 모든 코드가 같은 객체를 재사용함이 보장된다.


6-1 성능을 훨씬 더 끌어올릴 수 있다!

// 코드 6-1 성능을 훨씬 더 끌어올릴 수 있다!
    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은 입력받은 정규표현식에 해당하는 유한 상태 머신을 만들기 때문에 인스턴스 생성 비용이 높다

- 성능을 개선하려면 필요한 정규표현식을 표현하는 Pattern인스턴스를 클래스 초기화 과정에서 직접 캐싱해두고 나중에 isRomanNumeralSlow 매서드가 호출될 때마다 이 인스턴스를 재사용한다.


6-2 값비싼 객체를 재사용해 성능을 개선한다.

// 코드 6-2 값비싼 객체를 재사용해 성능을 개선한다.
    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();
    }

- isRomanNumeralFast 빈번히 호출되는 상황에서 성능을 상당히 끌어올릴 수 있다.

- 개선된 isRomanNumeralFast 방식의 클래스가 초기화된 후 이 매서드를 한 번도 호출하지 않는다면 ROMAN 필드는 쓸데없이 초기화 된 꼴이다.


6-3 끔찍이 느리다! 객체가 만들어지는 위치를 찾았는가?

private static long sum() {
        Long sum = 0L;
        for (long i = 0; i <= Integer.MAX_VALUE; i++)
            sum += i;
        return sum;
    }

- sum 변수를  long이 아닌 Long으로 선언해서 불필요한 Long 인스턴스가 약 2 ³¹ 개나 만들어진 것이다

- 박싱 된 기본 타입보다는 기본 타입을 사용하고, 의도치 않은 오토 박싱이 숨어들지 않도록 주의하자.

- 오토 박싱?

   - 프로그래머가 기본 타입과 박싱 된 기본 타입을 섞어 쓸 때 자동으로 상호 변환해주는 기술이다.

   - 오토 박싱은 기본 타입과 그에 대응하는 박싱 된 기본 타입의 구분을 흐려주지만, 완전히 없애주는 것은 아니다


프로그램의 명확성, 간결성, 기능을 위해서 객체를 추가로 생성하는 것이라면 일반적으로 좋은 일이다
그러나 무거운 객체가 아닌 다음에야 단순히 객체 생성을 피하고자 여러 개의 객체 풀(Pool)을 만들지는 말자
기본 객체를 재사용해야 한다면 새로운 객체를 만들지 마라!
방어적 복사가 필요한 상황에서 객체를 재사용했을 때의 피해가, 필요 없는 객체를 반복 생성했을 때의 피해보다 훨씬 크다!
728x90