Skip to content

자바 애플리케이션 성능 및 메모리 관리(JVM)

rnjstjdgh edited this page May 19, 2021 · 49 revisions

단순 지식 뿐 아니라 이걸 실제로 활용해 최적화 할 수 있는 포인트에 관심을 갖자

Course Overview

  • 두 가지 관점에서 생각

    1. Memory constraints(메모리 제약)
    2. application speed(속도)
  • java 언어적인 측면이 아니라 JVM 실행 환경적인 측면에 대한 내용이 주를 이룰 것임


2. Just in time compiler와 코드 캐시

  • JIT 컴파일러
    • 기본적으로 JVM은 바이트코드를 인터프리팅 방식으로 실행

      • 바이트코드를 직접 실행하기 때문에 native보다 느림
      • 인터프리팅 방식은 컴파일 방식보다 느릴 수 밖에 없음(한줄 한줄 실행하기 때문에 전체를 보며 최적화가 어려움)
    • JVM은 실행 환경을 프로파일링 하면서 [자주 실행되는 | 복잡한 | 시간이 많이 걸리는] 블록을 [bytecode => native code]로 컴파일 하며 최적화를 한다.

    • 프로파일링 및 컴파일 과정은 JVM내의 별도의 스레드를 통해 수행된다.

      • 따라서, 코드 실행 스레드에는 영향을 주지 않음
    • JIT 컴파일러가 어떻게 동작하느냐에 따라 성능이 달라질 수 있다.

      • 어떤 부분을 언제, 어느 수준으로 컴파일 하고 언제 caching할지??
      • 컴파일 할 때에도 0(컴파일 안함)~4까지의 수준이 있다.(jvm은 프로파일링을 통해 얻은 데이터를 기반으로 이 수준을 결정)
      • 이러한 요소 중에 개발자 수준 커스텀 가능한 요소가 있을까?!
    • Tuning the code cache size

      • level 4로 컴파일 되어 케싱이 될 수 있는 요소가 많은 프로그램의 경우 caching되어 있는 code block이 가득 차 있으며 실행중 일 수 있다. 이러면 더는 새로운 caching 후보가 있어도 cache하지 못하는 경우가 발생한다.
      • cache의 max size와 지금 사용되고 있는 size를 알 수 있으며, 지금 사용 되고 있는 size가 max에 근접하다면 cache size 증가를 고려해 볼 만 하다.
    • cache size, 컴파일이 어떻게 일어나는지 등은 application 실행 시 특정 flag 전달을 통해 확인할 수 있고 필요 시 외부 모니터링 툴과 연동할 수도 있음(그때그때 필요 시 찾아보면 되는 요소)

3. JVM선택

  • 실행 시 여러 flag를 통해 컴파일 옵션을 줄 수 있다. => 튜닝 가능한 지점

  • java -XX:+PrintFlagsFinal : java 실행 시 지정할 수 있는 flag들을 보여준다.

  • 성능에 영향을 줄 수 있는 요소(컴파일러 튜닝)

    1. 컴파일 프로세스를 실행하는 데 사용할 수 있는 스레드 수

      • CICompilerCount flag
      • jinfo -flag CICompilerCount [pid] 명령어를 통해 CICompilerCount의 기본 값을 알 수 있음
      • 기본값은 3개
      • [-XXLCICompilerCount=n] flag를 통해 실행되는 application의 컴파일 용 스레드 수를 지정할 수 있음
    2. 네이티브 컴파일의 임계값(동일한 메소드가 몇번 수행 될 때, 컴파일하는 것으로 판단 할 것인가?)

      • [-XX:CompileThreshold=n] flag를 통해 실행되는 application의 임계 값을 지정 가능
      • 기본값은 10000

정리

  1. jvm은 바이트코드를 실행하는 표준
  2. 바이트코드는 기본적으로 인터프리팅 방식으로 실행 => 느림
  3. 자주 실행되는 코드 블락을 컴파일 방식으로 실행해서 성능 개선을 도모
  4. 튜님 요소
    1. 자주 실행되는 기준을(임계 값을) 튜닝
    2. 컴파일에 사용되는 스레드 수를 튜닝
    3. code cache를 위한 cache size를 튜닝

4. How memory works - the stack and the heap

  • 성능에 가장 영향을 미치는 메모리 요소는 GC
  • GC를 이해하기 위해 JVM 메모리 동작 방식을 이해해야 한다.
  • 또한, JVM 메모리 동작 방식은 프로그래밍을 통해 얼마든지 커스터마이징 할 수 있는 요소가 많다!

  • (stack과 heap의 개념은 복습 측면이 강함)

5. 메소드 간 객체 전달

  • call by value(primitive) vs call by reference(object) 복습
  • final 키워드 학습
  • 참고자료: https://djkeh.github.io/articles/Why-should-final-member-variables-be-conventionally-static-in-Java-kor/
    • 기본 개념: final의 의미는 최종적이란 뜻을 가지고 있습니다. final 필드는 초기값이 저장되면 최종적인 값이 되어 프로그램 실행 도중에 수정을 할 수 없습니다.

      • final 필드:
        • 값 초기화 후 변경 불가
      • final 객체:
        • 객체 변수에 final로 선언하면 그 변수에 다른 참조 값을 지정할 수 없습니다. 즉 한번 생성된 final 객체는 같은 타입으로 재생성이 불가능합니다. 객체자체는 변경이 불가능하지만 객체 내부 변수는 변경 가능합니다.
      • final 클래스:
        • 최종상태가 되어 더이상 상속이 불가
        • final 클래스여도 필드는 Setter함수를 통하여 변경은 가능합니다.
      • final 메서드:
        • 메서드에 final을 사용하게되면 상속받은 클래스에서 부모의 final 메서드를 재정의 할 수 없습니다. 자신이 만든 메서드를 변경할 수 없게끔 하고싶을때 사용되며 시스템의 코어부분에서 변경을 원치 않는 메서드에 많이 구현되어 있습니다.
      • 메서드 인자 값에 final 사용:
        • final 필드와 마찬가지로 인자값에 final을 사용하는 경우 final 인자값의 변경이 불가능합니다.
    • 사용 이유:

      1. 코드에 의도를 명확하게 하기 위해
      2. final 키워드를 사용하면 자바 컴파일러가 최적화 할 수 있는 여지가 생김(final 키워드를 쓰면 변경 불가하기 때문에 인라이닝을 할 수 있다)
    • const vs final

      • 둘은 정확히 같은 개념? => 답은 아니다!

제목 c++ const java final
선언과 동시에 초기화? 초기화 해야 함 image 초기화 안해도 됨 image
객체의 참조의 경우 참조되는 객체의 상태를 변경하는 것이 가능? 불가 image image 가능 image
객체의 참조의 경우 참조되는 객체 자체를 바꾸는 것이 가능? 가능 image 불가능 image

  • 정리
    1. final변수의 의미는 한번만 참조를 지정할 수 있다는 것
    2. 즉, final변수가 참조하고 있는 객체의 상태는 언제든 바뀔 수 있다.
    3. final을 쓰면 컴파일러에 의해 선택적으로 인라인 처리가 될 수 있기 때문에 성능상의 이익을 얻을 수 있다(최적화 요소)