본문 바로가기
개발 공부/Java

이펙티브 자바 아이템 6 - 불필요한 객체 생성을 피하라 - 핵심 정리

by 개발인생 2022. 9. 22.
반응형

아이템 6 - 불필요한 객체 생성을 피하라 - 핵심 정리

이 글은 백기선 님의 이펙티브 자바 강의와 이펙티브 자바 3 / E 편을 참고하여 작성하였습니다.

주의할 점은 객체 생성은 비싸니 피하라 는 뜻이 아니다.

불필요하게 동일한 기능을 함에도 여러번 생성하는 경우가 있는 이런 상황을 피하도록 하자.

문자열

public class Strings {

   public static void main(String[] args) {
      String hello = "hello";

      //TODO 이 방법은 권장하지 않습니다.
      String hello2 = new String("hello");

      String hello3 = "hello";

      System.out.println(hello == hello2); // false - 같은 문자열임에도 불구하고 인스턴스가 다르기 때문에 false로 나온다
      System.out.println(hello.equals(hello2)); // true
      System.out.println(hello == hello3); // true
      System.out.println(hello.equals(hello3)); // true
   }
}

문자열은 "" 를 통해 생성한다.

하지만 new String("hello"); 을 통해 생성한다면 잘못 사용하고 있는 것이다.

JVM 은 내부적으로 문자열을 pool 에 캐싱하고 있다.

어디선가 동일한 문자열을 참조하려고 하면 새로 만드는 것이 아닌 상수들의 pool 에서

동일한 문자열을 참조하는 방법으로 재사용하게 된다.

하지만 new String 을 사용하면 강제적으로 새로운 객체를 생성하게 된다.

만약 같은 문자열임을 판단하는 경우에는 equals 를 사용하도록 하자.

정규 표현식

public class RomanNumerals {
   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})$");
   }

   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();
   }

   public static void main(String[] args) {
      boolean result = false;
      long start = System.nanoTime();
      for (int j = 0; j < 100; j++) {
         //TODO 성능 차이를 확인하려면 xxxSlow 메서드를 xxxFast 메서드로 바꿔 실행해보자.
         result = isRomanNumeralSlow("MCMLXXVI");
      }
      long end = System.nanoTime();
      System.out.println(end - start);
      System.out.println(result);
   }
}

정규식 같은 경우는 한번 생성할 때 만드는 비용이 좀 비싸다.

즉, Cpu 자원을 꽤 사용한다는 뜻이다.

자주 사용하는 패턴이고, 정규표현식이고, 여러번 사용된다면 필드로 선언해서 사용하는 걸 권장한다.

오토박싱(auto boxing)

프리미티브 타입을 wrapper 타입으로 변경할때 오토박싱 이 일어나고,

wrapper 타입을 프리미티브 타입으로 변환하는 과정을 언박싱 이라고 한다.

이러한 오토박싱, 언 박싱 을 런타임에 JVM 이 자동으로 처리해준다.

불필요하게 오토박싱 이 일어나는 경우 인스턴스가 계속 만들어지게 된다.

즉, long -> Long 으로 변환할 때 새로운 인스턴스가 만들어지게 된다.

public class Sum {
   private static long sum() {
      // TODO Long을 long으로 변경하여 실행해 보세요.
      Long sum = 0L;
      for (long i = 0; i <= Integer.MAX_VALUE; i++)
         sum += i;
      return sum;
   }

   public static void main(String[] args) {
      long start = System.nanoTime();
      long x = sum();
      long end = System.nanoTime();
      System.out.println((end - start) / 1_000_000. + " ms.");
      System.out.println(x);
   }
}

위 코드에서 불필요하게 오토박싱 이 일어나지 않게끔 코드를 수정하면 성능이 많이 개선된 걸 확인할 수 있다.

반응형

댓글