성능 지표

ENKRe.g.
Throughput처리율초당 처리된 트랜잭션
Latency지연평균 요청 처리 시간
Capacity수용량최대 동시 처리 가능 요청 수
Utilization사용률평균 CPU 사용률
Efficiency효율throughput / 비용
Scalability확장성리소스 추가에 따른 throughput 향상 정도
Degradation저하부하 증가에 따른 throughput 감소 정도

GC

용어

  • STW; Stop the world
  • GC를 위해 모든 애플리케이션 스레드를 정지하는 것

  • Safepoint
  • GC를 위해 스레드가 중단될 수 있는 지점. 스레드 내 고정된 자료구조를 볼 수 있다. JVM이 강제로 스레드를 safepoint 상태로 바꿀 수는 없다. 대신 스레드가 safepoint 상태에서 벗어나지 못하게 할 수는 있다

힙 = Young Generation + Old Generation

  • Young Generation
    1. Eden Space : 최초 생성된 객체들
    2. Survivor Space : Eden에서 수집 제외된 객체들
    3. From, To 두 단계로 구분되며, 이후 Tenured로 이동

    Young Generation이 큰 경우 덜 자주 수집되고, 더 적은 객체가 Old Generation으로 이동. 대신 Old Generation이 작아 풀 GC는 더 자주 일어난다

  • Tenured(Old) : Survivor에서 수집 제외된 객체들
  • Permanent Generation
    • 객체 GC 대상이 아닌 영역으로, PermGen이라고도 한다
    • 클래스 정의, String 상수 등 불변 객체들을 갖는다
    • Java 8에서 Metaspace로 변경됐다
    • 크기 한계(MaxMetaspaceSize)에 도달하면 dead class 정보는 삭제된다

GC 알고리즘 선정 시 유의사항

  • 각 GC로 인한 중단 기간
  • 처리율 : 전체 런타임 대비 GC 시간
  • 중단 빈도
  • 회수 효율
  • 중단 일관성 : 중단 기간 일정한지

GC 종류

  • 병렬 GC
  • 되도록 STW를 줄이기 위해 병렬 스레드 이용. 가능한 경우 애플리케이션과 동시 실행

  • Parallel GC
  • 단순한 영 세대용 병렬 GC

  • ParNew GC
  • CMS GC와 함께 사용할 목적으로 Parallel GC를 변형한 것

  • ParallelOld GC
  • 올드 세대용 병렬 GC. 객체를 재배치하여 메모리 단편화가 발생하지 않는다

  • CMS; Concurrent Mark Sweep
  • ↓bash

    java ... -XX:+UseConcMarkSweepGC # ParNew GC도 같이 작동한다
    • 올드 영역 GC의 STW를 짧게 하기 위해 개발됨
    • 메모리 압착을 하지 않아 단편화 발생 가능
    • 올드 영역 메모리가 아예 부족해진 경우(CMF; Concurrent Mode Failure) ParallelOld GC 실행
  • G1; Garbage First
  • ↓bash

    java ... -XX:+UseG1GC # Java 9부터는 default GC -XX:MaxGCPauseMillis=200 # 최대 중단 시간 목표치(ms). 즉 실제로는 넘을 수 있음
    • 대용량 힙에서 짧은 STW로 작동하는 수집기
    • 힙을 여러 영역(region)으로 나누고, 영/올드 세대를 불연속적으로 배치할 수 있다
    • 메모리 압착 수행

JVM 파라미터

  1. Boolean flag
  2. ↓bash

    XX:+FlagName XX:-FlagName
  3. Value flag
  4. ↓bash

    -XX:FlagName=value

주요 플래그

  • 힙 크기 관련
  • ↓bash

    -XmsN # 초기값 -XmxN # 최대값 -XX:MaxPermSize=N # ~ Java 7 -XX:MaxMetaspaceSize=N # Java 8 ~ # 힙 덤프 관련 -XX:+HeapDumpOnOutOfMemoryError # 메모리 부족시 힙 덤프 생성 -XX:HeapDumpPath={path} # 힙 덤프 작성 위치 -XX:+HeapDumpBeforeFullGC # 풀 GC 전에 힙 덤프 생성 -XX:+HeapDumpAfterFullGC # 풀 GC 후에 힙 덤프 생성 # 아래 부분은 알아서 잘 설정되며, Parallel GC에서 최후의 수단으로 조정 -XX:NewSize=N # Young Generation 초기 크기 -XX:MaxNewSize=N # Young Generation 최대 크기 -XmnN # NewSize, MaxNewSize 동일하게 설정 -XX:NewRatio=N # (Young Generation 초기 크기) = (힙 초기 크기) / (1 + NewRatio) -XX:SurvivorRatio=N # (Young Generation 크기) = N * (Survivor Space 크기) # MaxGCPauseMillis를 제외한 G1 튜닝을 하려면 아래 옵션을 켜야 한다 -XX:+UnlockExperimentalVMOptions

    힙 덤프 해석 툴 예 : heaphero

  • GC 관련
  • ↓bash

    -XX:ParallelGCThreads=N # 병렬 스레드 개수 -XX:GCHeapFreeLimit=N # 풀 GC로 힙의 N% 이상 해제되면 OK -XX:MaxTenuringThreshold=N # 테뉴어드 영역으로 승격되기 전까지 통과해야 하는 수집 횟수 # JDK 8 GC logging -XX:+PrintGCDetails # GC 이벤트 로깅 ON -XX:+PrintTenuringDistribution # 상세 정보 포함 -XX:+PrintGCTimeStamps # VM 이후 경과한 시간 -XX:+PrintGCDateStamps # 이벤트 시각(벽시계 기준) -XX:+PrintGCApplicationStoppedTime -Xloggc:/var/log/app/gc.log -XX:+UseGCLogFileRotation -XX:+NumberOfGCLogFiles=10 -XX:+GCLogFileSize=10m # JDK 9+ GC logging -Xlog:gc*,gc+age=trace,safepoint:file=/var/log/app/gc.log:utctime,pid,tags:filecount=10,filesize=10m

    GC 로그는 GCViewer 등으로 시각화 가능

  • JIT 관련
  • ↓bash

    -XX:ReservedCodeCacheSize=N # 최대 코드 캐시 크기. 최초 설정 후 동적으로 확장되지 않는다 -XX:+PrintCompilation # 어떤 메서드가 JIT 컴파일 됐는지 STDOUT 출력
  • Default networking timeout
  • ↓bash

    -Dsun.net.client.defaultConnectTimeout=2000 # 2000ms -Dsun.net.client.defaultReadTimeout=2000 # 2000ms

Java 옵션(JAVA_OPTS)

  1. user.timezone 프로퍼티 기본값 지정
  2. ↓shell

    $ java -Duser.timezone=Asia/Seoul
  3. 인코딩 관련 설정
  4. ↓shell

    $ java -Dfile.encoding=UTF-8 -Dfile.client.encoding=UTF-8 -Dclient.encoding.override=UTF-8
  5. 리모트 디버깅 설정
  6. ↓shell

    # Before JDK 1.4 $ java -Xnoagent -Djava.compiler=NONE -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address="*:55555" # For JDK 1.4 $ java -Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,address="*:55555",server=y,suspend=n # address : 수신 허용 범위 # server : 서버로 작동할지 여부 # suspend : 디버거 부착까지 기다릴지 여부 # After JDK 1.4 $ java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address="*:55555"

jcmd Since Java 8

JDK 기본제공 분석 도구

  1. 자바 프로세스 목록
  2. ↓shell

    $ jcmd
  3. 사용 가능 명령
  4. ↓shell

    $ jcmd {pid} help
  5. 힙 히스토그램
  6. ↓shell

    $ jcmd {pid} GC.class_histogram # GC 실행. 실행 후 힙 상태 $ jmap -histo {pid} # GC 실행 X. 현재 힙 상태
  7. 힙 덤프
  8. ↓shell

    $ jcmd {pid} GC.heap_dump ./out.hprof
  9. 시스템 프로퍼티
  10. ↓shell

    $ jcmd {pid} VM.system_properties
  11. 쓰레드 덤프
  12. ↓shell

    $ jcmd {pid} Thread.print

jhsdb Since Java 9

JDK 기본제공 디버거; Java Hotspot Debuggerjhsdb

  1. JVM에 디버거 부착 시 실행이 일시중지됨에 유의
  2. 사용
  3. ↓shell

    # clhsdb 커맨드라인 디버거 $ jhsdb clhsdb [--pid pid | --exe executable --core coredump] # hsdb GUI 디버거 $ jhsdb hsdb [--pid pid | --exe executable --core coredump] # debugd 리모트 디버그 서버 시작 $ jhsdb debugd [options] pid [server-id]|[option] executable core [server-id]

VisualVM

개요

커맨드라인 JDK 툴과 가벼운 프로파일 기능을 포함하는 자바 프로세스 시각화 툴

Remote 프로세스 연결

  1. Add Remote Host
  2. 호스트 주소 입력

  3. Add JMX Connection or Add jstatd Connection
  4. ↓JMX 활성화

    -Dcom.sun.management.jmxremote.local.only=false \ -Dcom.sun.management.jmxremote.host=172.159.246.217 \ # ifconfig로 확인 -Dcom.sun.management.jmxremote.port=11099 \ -Dcom.sun.management.jmxremote.rmi.port=11099 \ -Dcom.sun.management.jmxremote.ssl=false \ -Dcom.sun.management.jmxremote.authenticate=false \ -Djava.rmi.server.hostname=172.159.246.217 \

마이크로 벤치마크 유의사항