티스토리 뷰

반응형

객체의 재사용

똑같은 기능의 객체를 매번 생성하는 것보다 객체 하나를 재사용하는 것이 나을 때가 많음

생성자 대신 정적 팩터리 메서드를 사용하여 불필요한 객체 생성을 피할 수 있음 ex) Boolean.valueOf(String)

그런데 생성 비용이 아주 비싼 객체도 있다.

이런 객체를 반복해서 필요할 때는 캐싱해서 재사용을 권장한다.

static boolean isRomanNumeral(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 인스턴스를 클래스 초기화 과정에서 생성하여 캐싱해두고, isRomalNumeral 메서드가 호출될때마다 재사용한다.

// 미리 객체를 생성하여 캐싱해두고, 재사용하는 방식으로 개선
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 s) {
    return ROMAN.matcher(s).matches();
}

 

 

어댑터

객체가 불변이라면 재사용해도 안전함이 명백하지만, 덜 명확한 상황도 있다.

뷰라고도 하는 어댑터를 생각해보자.

어댑터는 실제 작업은 뒷단 객체에 위임한채 자신은 제 2의 인터페이스 역할을 하는 객체를 말한다.

어댑터는 뒷단 객체 하나당 하나씩만 만들어지면 충분하다.

public class AdapterExample {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("one", 1);
        map.put("two", 2);

        Set<String> keys1 = map.keySet();
        Set<String> keys2 = map.keySet();

        //반환받은 Set 객체 중 하나를 수정하면 다른 모든 객체도 따라서 수정된다.
        keys1.remove("one");
        System.out.println(keys2.size()); //1
        System.out.println(map.size()); // 1
    }
}

Map 인터페이스가 제공하는 keySet 메서드는 Map 객체 안의 키 전부를 담은 Set 뷰를 반환한다.

keySet을 호출할 때마다 새로운 객체가 만들어질 것 같지만 사실 같은 객체를 반환한다.

반환된 Set 객체가 일반적으로 가변이더라도 반환된 객체들은 기능적으로 모두 똑같다.

즉, 반환받은 객체 중 하나를 수정하면 다른 모든 객체가 따라서 바뀐다 (모두가 같은 Map 인스턴스를 대변하기 때문).

결국에 그 뒤에 있는 Map 객체를 변경하게 된다.

 

 

오토박싱(auto boxing)

오토박싱은 기본타입과 박싱된 기본 타입을 섞어 쓸 때 상호변환해주는 기술인데, 타입의 구분을 완전히 없애주는 것은 아니어서 성능면에서 달라진다.

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

위 코드는 sum 변수를 long이 아닌 Long으로 선언을 했기 때문에 성능이 훨씬 느려진다.

i가 for문에서 sum에 더해질때마다 불필요한 Long 인스턴스가 만들어지기 때문이다.

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

 

 

결론

명확성, 간결성, 기능을 위해서 객체를 추가로 생성하는 것이라면 일반적으로 좋은일이다.

아주 무거운 객체가 아니고서야 단순히 객체 생성을 피하려고 객체 풀을 만들지는 말자.

요즘 JVM의 가비지 컬렉터는 잘 최적화되어 있기 때문에 크게 부담을 주지 않는다.

 

[아이템 50]

방어적 복사가 필요한 상황에서 객체를 재사용했을 때 피해가 필요없는 객체를 반복 생성했을 때 피해보다 더 크다.

방어적 복사에 실패하면 버그와 보안 문제로 이어지지만, 불필요한 객체 생성은 코드 형태와 성능에만 영향을 주기 때문이다.

반응형
댓글
반응형
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday