이펙티브 자바 완벽 공략 1부

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

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

핵심 정리

  • 문자열
    • 사실상 동일한 객체라서 매번 새로 만들 필요가 없다
    • new String(“자바”) 을 사용하지 않고 문자열 리터럴 (“자바”)을 사용해 기존에 동일한 문자열을 재사용하는 것이 좋다
    • JVM은 내부적으로 문자열을 다 풀에서 캐싱을 하고 있다 일종의 해시 맵에 한번 만들어진 문자열들을 다 담아놓고 또 어디선가 동일한 문자열을 참조하려고 하면 그 동일한 문자열을 새로 만드는 게 아니라 이미 만들어놓은 그 컨스턴트 풀 상수들의 풀에서 그 동일한 문자를 참조하는 방법으로 문자를 재사용하기 때문에이다 new String 강제적으로 새로운 문자를 만든다
  • 정규식, Pattern
    • 생성 비용이 비싼 객체라서 반복해서 생성하기 보다, 캐싱하여 재사용하는 것이 좋다.
    • 정규식 같은 경우는 한번 만들 때 만드는 비용이 꽤 비싸다 CPU 리소스를 사용한다
    • 패턴을 컴파일해서 패턴 인스턴스를 만드는 과정이 조금 오래걸린다

// 값비싼 객체를 재사용해 성능을 개선한다. (32쪽)
public class RomanNumerals {
    // 코드 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})$");
    }

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

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


  • 오토박싱 (auto boxing)
    • 기본 타입(int)을 그게 상응하는 박싱된 기본 타입(Integer)으로 상호 변환해주는 기술
    • 기본 타입과 박싱된 기본 타입을 섞어서 사용하면 변환하는 과정에서 불필요한 객체가 생성될 수 있다

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


  • “객체 생성은 비싸니 피하라.” 는 뜻으로 오해하면 안 된다

완벽 공략

  • p31, 사용 자제 API (Deprecation)
  • p32, 정규 표현식
  • p32, 한 번 쓰고 버려져서 가비지 컬렉션 대상이 된다
  • p33, 초기화 지연 기법 (아이템 83에서 다룸)
  • p34, 방어적 복사 (아이템 50에서 다룸)

완벽 공략 16 - deprecation

클라이언트가 사용하지 않길 바라는 코드가 있다면…

  • 사용 자제를 권장하고 대안을 제시하는 방법이 있다
  • @Deprecated
    • 컴파일시 경고 메시지를 통해 사용 자제를 권장하는 API라는 것을 클라이언트에 알려줄 수 있다.
  • @deprecated
    • 문서화(Javadoc)에 사용해, 왜 해당 API 사용을 지양하며, 그 대신 권장하는 API가 어떤 것인지 표기할 수 있다.

public class Deprecation {

    /**
     * @deprecated in favor of
     * {@link #Deprecation(String)}
     */
    @Deprecated(forRemoval = true, since = "1.2")
    public Deprecation() {
    }

    private String name;

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

완벽 공략 17. 정규 표현식

내부적으로 Pattern이 쓰이는 곳

완벽 공략 18. 가비지 컬렉션

기본 개념

  • Mark, Sweep, Compact
    • Mark는 더 이상 어떤 오브젝트가 계속해서 참조를 가지고 있는지 아닌지 체크해 놓는 것 사라져도 되는 인스턴스인지 체크를 해놓는 것 더 이상 레퍼런스 하는 애가 없다면 사라져도 되는 것이다
    • Sweep 필요 없는 오브젝트를 실제로 메모리 공간 힙에서 날리는 것이다
    • Compact 여기 여기 부분 부분 나눠져 있는 것들을 합치는 과정
  • Young Generation (Eden, S0, S1), Old Generation
    • 객체들은 보통 생명주기가 굉장히 짧다 오래 살아남는 객체가 그렇게 많지는 않다 애플리케이션에서 객체가 보통 만들어주고 한 메소드 내에서 죽는 객체도 되게 많고 한 메소드에서 죽지 않고 오래 살아남는 객체는 그렇게 많지는 않다
  • Minor GC, Full GC
    • Minor GC가 Young Generation에서 일어나는 GC를 이야기하고 Full GC가 Old Generation과 Young Generation 같이 GC를 하는 아주 대규모 청소 작업인데 이 대규모 청소 작업이 굉장히 중요하다
  • Throughput, Latency (Stop-The-World), Footprint
    • Throughput, latency, footprint. 세 가지 관점에서 각각의 GC 로직을 봐야한다
    • Throwput 이라는 것은 애플리케이션 처리할 수 있는 처리양 각각의 GC 알고리즘이 throw put에 얼마만큼의 영향을 주느냐
    • Latency를 이야기할 때 빠질 수 없이 나오는 말이 stop the world라는 말이다 GC가 일어나는 경우에는 실제 컴팩트하는 과정이 특히 그런데 멈춘다
    • Footprint은 얼마만큼 GC가 애플리케이션 사용하는 메모리이다 얼마나 많은 저장 공간을 필요로 하느냐.
  • Serial, Parallel, CMS, G1, ZGC, Shenandoah
    • Java 8에는 Parallel GC 그리고 Java 11 버전은 g1 gc
    • CMS는 자바 9버전부터 디플리케이티드가 됐고 아마 13인가 14버전부터는 아예 없어졌다
  • 참고) How to choose the best Java garbage collector

© 2020. All rights reserved.

SIKSIK