JAVA/Effective

[Effactive Java] 불필요한 객체 생성을 피하라 - 6

빅콜팝 2023. 1. 8. 23:06
728x90
반응형

다음과 같은 세 가지 경우에서 불필요한 객체 생성을 피하자

  • 문자열
  • 정규식, Pattern
  • 오토박싱(auto boxing)

문자열


JVM은 내부적으로 문자열을 해시맵 풀에 담아두고 어디선가 동일한 문자열을 참조하면 만들어 놓은 풀에서 참조하는 방법으로 재사용하고 있다.
그렇기에 문자열 생성에 new String("hello")를 사용한다면 불필요한 객체를 생성하게 된다.

  • 사실상 동일한 객체라서 매번 새로 만들 필요가 없다.
  • new String("자바")을 사용하지 않고 문자 리터럴 ("자바")을 사용해 기존에 동일한 문자열을 재사용하는 것이 좋다.
public class Strings {
    public static void main(String[] args) {
        String hello1 = "hello";
        String hello2 = new String("hello");
        String hello3 = "hello";

        System.out.println(hello1 == hello2); // false
        System.out.println(hello1 == hello3); // true
    }
}

정규식, Pattern


정규식의 경우 한번 만들 때 많은 비용을 소모한다.
CPU 리소스를 사용하여 패턴을 매칭할 때 패턴을 컴파일하여 인스턴스를 만드는 과정이 오래 걸린다.
그러므로 동일한 패턴을 여러 번 사용한다면 필드로 선언하여 재사용하도록 하자.

public class RomanNumerals {
    static boolean isRomanNumeralSlow(String s){
        return s.matches("^[a-zA-Z]*$");
    }
    private static Pattern ROMAN = Pattern.compile("^[a-zA-Z]*$");
    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 < 50; j++){
            // 성능 차이를 확인하려면 xxxSlow 메서드를 xxxFast 메서드로 바꿔 실행
            result = isRomanNumeralSlow("XCMLXXVI");
        }

        long end = System.nanoTime();
        System.out.println(end - start);
        System.out.println(result);
    }
}

오토 박싱(auto boxing) / 오토 언박싱(auto unboxing)


기본 타입 : int, long, float, double, boolean 등
Wrapper 클래스 : Integer, Long, Float, Double, Boolean 등

박싱 : 기본 타입 데이터에 대응하는 Wrapper 클래스로 만드는 동작
언박싱 : Wrapper 클래스에서 기본 타입으로 변환

자바에서는 편의성을 위해 오토 박싱과 오토 언박싱을 제공하고 있다. 하지만 내부적으로 추가 연산 작업을 거쳐 변화되어 많은 실행 시간을 소모하게 된다. 그러므로 불필요한 오토 박싱과 언박싱이 일어나지 않도록 동일한 타입 연산이 이루어지도록 구현하자.

public class Sum {
    private static long sum(){
        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);

//        4054.309334ms. -> 오토박싱 사용O
//        2305843008139952128

//        706.422041ms. -> 오토박싱 사용X
//        2305843008139952128
    }
}


deprecation

사용하지 않을 메서드에 @Deprecated 어노테이션을 이용해 해당 메서드를 사용하려 할 때 경고를 보내준다.

public class Deprecation {
    /**
     * @deprecated in favor of
     * {@link #Deprecation(String)}
     */
    @Deprecated
    public Deprecation(){}
    private String name;

    public Deprecation(String neme){
        this.name = neme;
    }
}

여기까지는 다들 아는 기능일텐데
자바9 버전부터는 forRemoval = true 프로퍼티를 사용해 삭제될 코드라는 좀 더 강한 경고를 보낼 수 있다.

public class Deprecation {
    /**
     * @deprecated in favor of
     * {@link #Deprecation(String)}
     */
    @Deprecated(forRemoval = true)
    public Deprecation(){}
    private String name;

    public Deprecation(String neme){
        this.name = neme;
    }
}


정규 표현식

내부에서 만드는 정규표현식 패턴 인스턴스는 한번 쓰고 버려져서 곧바로 가비지 컬렉션의 대상이 된다.
그러므로 Pattern을 만들어 재사용 하는 것이 바람직하다.

내부적으로 Pattern이 쓰이는 곳은 다음과 같다.
• String.matches(String regex)
• String.split(String regex)
- 대안, Pattern.compile(regex).split(str)
• String.replace*(String regex, String replacement)
- 대안, Pattern.compile(regex).matcher(str).replaceAll(repl)


split에서 char 타입인 경우에는 split 내부 동작에 의해 패턴을 사용하지 않고 바로 사용하는 것이 오히려 더 빠르다.

public class RegularExpression {
    private static final Pattern SPLIT_PATTERM = Pattern.compile(",");

    public static void main(String[] args) {
        long start = System.nanoTime();
        for(int i=0; i<1000; i++){
            String name = "hello, effective";
            name.split(",");
//            SPLIT_PATTERM.split(name);
        }
        System.out.println(System.nanoTime() - start);
    }
}


그 외의 경우에는 패턴을 캐싱하여 재사용하는 것이 성능에 좋다.


정규 표현식 Document
https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html
https://docs.oracle.com/javase/tutorial/essential/regex/

정규 표현식 테스트 사이트
https://regex101.com/
https://regexr.com/

reference

https://www.inflearn.com/course/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C-%EC%9E%90%EB%B0%94-1/dashboard

728x90
반응형