이펙티브자바, 불필요한 객체 생성은 피하라

박싱타입과 기본타입의 성능 문제

  • 대체로 객체를 생성하는 것은 좋다. 기본타입에서도 마찬가지인데 null을 사용할 수 있는 등 객체로서 동작한다.
  • 하지만 기본타입을 객체로 박싱하기 때문에 이로 인한 성능 상 문제가 있다.
  • 아래의 코드는 전자가 후자보다 약 5배 느리다.
public class C06Test {
    @Test
    void test(){
        wrapper();
        primi();
        wrapper();
        primi();
    }

    private void wrapper() {
        final long startTime = System.currentTimeMillis();
        final int size = Integer.MAX_VALUE;
        Long sum1 = 0l; // 박싱 타입이다. 
        for(int i = 0; i< size; i++){
            sum1 += Integer.MAX_VALUE*i;
        }
        final long endTime = System.currentTimeMillis() - startTime;
        System.out.println("endTime with obj =" + endTime);
    }

    private void primi() {
        final long startTime = System.currentTimeMillis();
        final int size = Integer.MAX_VALUE;
        long sum1 = 0l; // 기본 타입이다.
        for(int i = 0; i< size; i++){
            sum1 += Integer.MAX_VALUE*i;
        }
        final long endTime = System.currentTimeMillis() - startTime;
        System.out.println("endTime with primitive type =" + endTime);
    }
}

객체가 아닌 정적 매서드도 효과적이다.

  • 아래의 코드는 ObjectMapper를 하나를 만들어 사용하는 것과, 사용할 때마다 객체를 생성하는 코드이다.
  • 속도 차이는 약 6 배 정도 발생했다.
@Test
void test3() throws JsonProcessingException {
    // given
    List<Member> members = new ArrayList<>();
    for (int i = 0; i < 1000; i++) {
        members.add(new Member());
    }

    // test1
    final long startTime = System.currentTimeMillis();
    for(int i=0; i<1000; i++){
        parseJson(members);
    }
    final long endTime = System.currentTimeMillis() - startTime;
    System.out.println("endTime, generate instance each time =" + endTime);

    // test2
    final long startTime2 = System.currentTimeMillis();
    for(int i=0; i<1000; i++) {
        parseJsonReusable(members);
    }
    final long endTime2 = System.currentTimeMillis() - startTime2;
    System.out.println("endTime, reusable static util = " + endTime2);

}

String parseJson(List<Member> members) throws JsonProcessingException {
    final ObjectMapper objectMapper = new ObjectMapper();
    final String result = objectMapper.writeValueAsString(members);
    return result;
}
static final ObjectMapper objectMapper = new ObjectMapper();

String parseJsonReusable(List<Member> members) throws JsonProcessingException {
    final String result = objectMapper.writeValueAsString(members);
    return result;
}

@Data
static class Member {
    private String name;
    private int age;

    public Member() {
        name = UUID.randomUUID().toString();
        age = (int) (Math.random() * Integer.MAX_VALUE);
    }
}

다만, 보조적으로 사용한다.

  • 현재 JVM의 성능의 매우 좋다. 현 시점에서 객체를 반복 생성하여 얻는 손해보다, 객체를 재사용하다가 발생한 문제가 더 심각하다. 그러므로 객체의 반복사용은 고민해야 한다.
  • 다만 위의 예제의 경우 static final로서 유일함을 보장하고, 유틸로서의 기능을 하는 경우 사용하기에 좋다.