1장 자바 성능 분석 개요 및 핵심 원리

1. 자바 성능의 본질

성능 엔지니어의 목표 JVM 튜닝(플래그 설정)과 플랫폼 기능(코드 작성법) 두 가지 영역을 모두 이해해 최적의 성능을 이끌어 내는 것!!

JVM이란?

2. 자바 플랫폼과 JVM 튜닝 플래그

JVM 튜닝 플래그 문법

  • 불리언(Boolean) 플래그 기능을 켜고 끄는 용도
    • 켜기: -XX: +FlagName(+ 기호 사용)
    • 끄기: -XX: -FlagName(- 기호 사용)
  • 파리미터(Parameter) 플래그: 특정 값을 설정하는 용도
    • 형식: -XX:FlagName=Value (ex. -XX:NewRatio=N)

플래그란?

에르고노믹스(Ergonomics)

JVM이 하드웨어 환경(CPU 수, 메모리 등)을 감지해 튜닝 플래그의 기본갑을 자동으로 설정하는 과정을 뜻함

  • 확인 방법: -XX:+PrintFlagsFinal 플래그를 사용해 현재 적용된 모든 플래그의 최종 값을 확인할 수 있음

3. 하드웨어 플랫폼과 하이퍼 스레딩

하이퍼 스레딩이란?

하나의 코어가 두 개의 스레드를 동시에 처리하는 것처럼 보이게 하는 기술 가상 스레드??

  • 성능 효율성
    • OS나 JVM은 이를 2배의 CPU로 인식하지만 실제 성능이 2배가 되지는 않음
    • 일반적으로 단일 코어 대비 약 ==56배==의 성능 향상(가상 CPU 8개 기준)을 보임 즉 물리 코어 성능의 100% 효율이 아닌 약 2040% 정도의 추가 효율을 냄

4. 소프트웨어 컨테이너와 JVM

가상 머신(VM)과 컨테이너

클라우드 환경에서는 JVM이 가상 머신이나 도커 컨테이너 안에서 실행되는 경우가 많음

  • 버전별 리소스 인식 문제
    • 최신버전(Java8 이후) 컨테이너에 할당된 CPU와 메모리 제한을 올바르게 인식해 Heap 크기나 스레드 수를 자동으로 조절함
    • 구 버전(초기 Java8) 컨테이너 제한이 아닌 호스트 머신의 전체 리소스를 인식 이로 인한 너무 큰 Heap이나 많은 스레드를 생성해 Out Of Memory 오류로 컨테이너가 강제 종료될 위험이 있었음

5. 성능 향상을 위한 코딩 원칙

성능 튜닝의 외적인 요소

1. 더 나은 알고리즘 작성

가장 중요한 요소임 아무리 JVM을 튜닝해도 비효율적인 알고리즘(ex. 검색에 배열 루프 사용)은 HashMap을 사용하는 것보다 느림

HashMap이 빠른 이유는?

2. 코드 양 줄이기

컴파일할 코드가 많을수록, 할당되는 객체가 많을수록 JVM의 부담이 커짐 작은 성능 저하가 모여 큰 문제를 만드는 상황을 피해야 한다.

3. 섣부른 최적화에 대한 오해

섣부른 최적화는 만악의 근원이지만 비효율적인 코드를 방치하라는 것은 아니다

  • 원칙 : 코드는 깔끔하고 읽기 쉬워야 하지만 명백히 비효율적인 구조(ex. 불필요한 루프, 로깅 레벨 확인 없는 문자열 연결 등)은 처음부터 피해야한다

4. DB 병목

  • 외부 리소스(DB)가 병목일 때 자바 애플리케이션만 튜닝하는 것은 효과가 없거나 오히려 역효과다

즉 전체 시스템 관점에서 병목 지점을 먼저 찾아야 한다.

5. 일반적인 경우에 최적화하라

  • 코드 프로파일링을 통해 가장 자주 실행되는 작업이나 리소스를 많이 점유하는 부분을 찾아 집중적으로 최적화 해야한다.
  • 문제 해결 시에는 오컴의 면도날 원칙을 적용해 가장 가능성 높은 단순한 원인(새로 작성한 코드의 버그 등)부터 의심해야한다.

2장 성능 테스트 접근법

성능 최적화의 기반이 되는 성능 테스트의 4대 원칙과 이를 적용하는 방법을 다룬다.

1. 원칙 1 실제 응용 프로그램을 테스트하라

성능 테스트는 코드가 실제 환경에서 어떻게 동작하는지 반영해야한다.

마이크로벤치마크

  • 정의 : 아주 작은 단위(ex. 메서드)의 성능을 측정하는 테스트
  • 함정:
    • 컴파일러 최적화 : 결과값이 사용되지 않으면 컴파일러가 코드를 삭제할 수 있음 Volatile 변수 사용이나 JMH의 Blackhole로 해결 필요
    • 워밍업 : 자바는 실행되도록 JIT 컴파일러가 최적화하므로 초기 측정값은 부정확함
    • 멀티스레드 영향 : 동기화 경합이 실제 환경과 다르게 측정될 수 있ㅇ므

매크로벤치마크

  • 정의 : DB 등 외부 자원을 포함한 전체 시스템을 테스트하는 것 가장 정확한 결과를 제공

메조벤치마크

  • 정의 : 실제 작업의 일부(ex. 특정 REST 호출)만 수행하는 중간 단계 테스트 모듈별 성능 파악에 유리

JIT란?

JMH란?

마이크로벤치마크와 메조 벤치마크는 단위 테스트를 뜻하는가??

원칙 2 처리량, 배치, 응답 시간을 이해하라

성능 측정 지표는 상호 연관되어 있으며 목적에 맞게 선택해야한다

  • 경과 시간(Elapsed Time, Batch) : 특정 작업을 완료하는 데 걸리는 총 시간
  • 처리량(Throughput) : 일정 시간 동안 완료한 작업량 TPS, RPS로 표현
  • 응답 시간 : 요청 후 응답을 받기까지의 지연 시간
    • 평균 vs 백분위 : 평균은 이상치에 왜곡될 수 있으므로 90th%, 99th% 응답 시간을 확인하는 것이 실제 사용자 경험 파악에 더 중요

원칙 3 가변성을 이해하라

모든 테스트 결과는 실행할 때마다 변하므로 통계적 유의성을 확인해야 한다.

1. 회귀 테스트

기존과 수정본의 성능을 비교하는 것

2. Student’s t-test 분석

  • p-value : 두 집간의 평균이 같다는 가설이 참일 확률
  • 유의 수준(α) : 보통 0.1(90% 신뢰도) 또는 0.05(95% 신뢰도)를 사용
  • 판단: p-value가 유의 수준보다 낮으면 “성능 변화가 실질적”이라고 판단

3. 통계적 유의성 vs 중요성

결과가 통계적으로 확실하더라도(유의성), 성능 향상 폭이 1% 미만이라면 투자 대비 가치(중요성)가 낮을 수 있음

원칙 4 조기에 빈번하게 테스트하라

성능 문제는 나중에 수정할수록 비용이 기하급수적으로 증가함

  • 모든 것을 자동화하라 : 스크립트를 통해 환경 설정, 실행, 분석(t-test포함)을 자동화해야함
  • 모든 것을 측정하라 : CPU 사용량뿐 아니라 GC 로그, 디스크 I/O, 네트워크 사용량, JFR 기록 등을 수집해야 원인 분석이 가능

실전 도구 JMH(Java Microbenchmark Harness)

자바 마이크로벤치마크의 복잡성을 해결하기 위해 개발된 표준 프레임워크

  • 주요 어노테이션 및 기능
    • @Benchmark : 성능 측정 대상 메서드 지정
    • @Setup : 테스트 전 초기화 코드(데이터 생성 등)
    • @Param : 다양한 입력 파리미터 값 설정 가능
    • Blackhole : 죽은 코드 삭제 방지를 위해 결과값을 소비함
    • Trial & Fork : 별도의 JVM 프로세스(Fork)에서 여러 번 반복 실행(Trial)하여 결과의 안전성 확보

암기 포인트

  1. 마이크로벤치마크의 3대 주의점: 워밍업 부족, 결과 미사용(DCE), 동기화 경합.

  2. 응답 시간 측정 시 필수 지표: 평균값뿐만 아니라 90/99 백분위수(Percentile) 확인.

  3. 통계적 판단 근거: p-value와 신뢰 구간을 통한 유의성 검증.

  4. JMH 사용 이유: JIT 컴파일러 최적화 방해 방지 및 통계적 실행 제어


3장 자바 성능 도구함

성능 분석의 핵심은 가시성이며 이를 위해 OS및 자바 전용 도구를 적재적소에 활용하는 능력이 필수적이다

1. OS 모니터링 도구

JVM 외부 환경을 먼저 점검해 병목 지점이 OS 자원에 있는지 확인한다

CPU 사용량

  • 사용자 시간 : 애플리케이션 코드를 실행하는 시간
  • 시스템 시간 : 커널 코드를 실행하는 시간(ex. I/O 처리 등)
  • 대기 큐 : 실행 대기 중인 스레드 수 CPU 개수보다 많으면 시스템 과부하 상태를 의미

디스크와 네트워크, I/O

  • iowait : CPU가 디스크 응답을 기다리는 시간시간 100%에 가까우면 디스크가 병목임
  • 대역폭(Bandwidth): 네트워크 인터페이스의 초당 데이터 전송량

2. 자바 기본 모니터링 도구

JDK에 기본 포함된 명령행 도구들임

JCMD

JVM의 상태를 확인하는 맥가이버 칼 같은 도구 플래그 확인, 스레드 덤프, GC 실행 등 수행

JSTAT

GC 활동 및 클래스 로딩 정보를 실시간 숫자로 확인

JMAP

Heap Dump 생성 및 힙 메모리 통계 확인

JSTACK

현재 실행 중인 모든 스레드의 스택 추적 정보 확인

-XX:+PrintFlagsFinal

JVM 시작 시 모든 튜닝 플래그의 기본값과 현재 적용 값을 출력

3. 프로파일링 도구

애플리케이션 내부에서 어디가 느린지 상세 분석하는 도구

샘플링 프로파일러

주기적으로 스레드 스택을 덤프해 통계 추출 오버헤드가 적음

  • 세이프 포인트 편향 : JVM이 세이프포인트에서만 스택을 추출해 발생하는 통계적 외곡
  • Async 프로파일러 : 세이프포인트 편향을 피하기 위해 AsyncGetCallTrace를 사용해 정확도를 높임

인스트루먼테이션 프로파일러

코드의 모든 메서드 호출 전후에 측정 코드를 삽입 호출 횟수를 정확히 알 수 있으나 오버헤드가 매우 큼

플레임 그래프

CPU 사용량을 시각적으로 보여주는 그래프 아래가 넓을수록 해당 메서드군이 CPU를 많이 사용함

4. Java Flight Recorder(JFR) & Mission Control (JMC)

JVM 내부에 통합된 가장 강력한 분석 도구 세트

JFR

JVM 내부 이벤트를 기록하는 비행 기록 장치 오버헤드가 1% 미만으로 실운영 환경에서 사용 가능

JMC

JFR이 기록한 데이터를 분석하는 시각화 GUI 도구

핵심 이벤트

GC 상세 시간, TLAB 할당량, 모니터 락 경합 정보 등 제공

4장 예습

  1. JIT 컴파일이란?
    • 자바 바이트코드가 실행 시점에 어떻게 기계어로 변환되는지 개념 파악.
  2. 계층형 컴파일 (Tiered Compilation):
    • 빠르게 컴파일하는 C1 컴파일러와 고도의 최적화를 수행하는 C2 컴파일러의 차이점.
  3. 코드 캐시 (Code Cache):
    • 컴파일된 기계어 코드가 저장되는 공간의 중요성.
  4. 최적화 기법 (Inlining & Escape Analysis):
    • 메서드 본문을 호출부에 삽입하는 ‘인라이닝’과 객체의 범위를 분석하여 힙 할당을 피하는 ‘탈출 분석’의 기본 개념.

4장 JIT 컴파일러 작동 원리

자바 성능의 핵심인 JIT(Just In Time)

1. JIT 컴파일러 개요

컴파일 방식

자바는 플랫폼 독립적인 바이트코드로 컴파일된 후 실행 시점에 JVM이 해당 OS에 맞는 기계어로 변환한다

HotSpot 컴파일

모든 코드를 컴파일하지 않고 실행 횟수가 많은 HotSpot 영역만 컴파일해 효율을 극대화한다

컴파일의 이유

초기 해석 단계(interpreted)보다 컴파일 후 실행 속도가 훨씬 빠르며 실행 중 얻은 정보를 바탕으로 더 고도의 최적화가 가능하기 때문

2. 계층형 컴파일

JVM은 두 가지 서로 다른 컴파일러를 함께 사용함

C1 컴파일러(Client)

컴파일 속도는 빠르지만 최적화 수준은 낮음 프로그램 시작 시 빠르게 성능을 높이는데 적합

C2 컴파일러(Server)

컴파일 속도는 느리지만 실행 정보를 바탕으로 고도의 최적화를 수행

계층 구조

코드는 Level0(해석) Level 3(C1) Level 4(C2)

3. 코드 캐시

컴파일된 기계어 코드가 저장되는 JVM의 네이티브 메모리 영역

  • 튜닝 플래그 : -XX:ReservedCodeCacheSize=N으로 최대 크기를 설정
  • 위험성 : 코드 캐시가 가득 차면 JVM은 더 이상 코드를 컴파일할 수 없으며 성능이 심각하게 저하됨

가득차는 조건과 그럼 캐시가 지워지는 타이밍은 언제인가??

4. 컴파일 모니터링 및 로그

  • PrintCompilation : -XX:+PrintCompilation 플래그를 통해 컴파일되는 메서드와 레벨을 실시간으로 확인 가능
  • 중요 속성
    • %(OSR) : 루프가 너무 많이 실행되어 실행 중에 컴파일된 코드로 교체됨(On-Stack Replacement)
    • s(Synchronized) : 동기화 메서ㅔ드
    • ! (Exception) : 예외 처리 포함

5. 역최적화(Deoptimization)

컴파일러가 내린 최적화 가정이 틀렸을 때(ex. 인터페이스의 다른 구현체가 나타남), 컴파일된 코드를 버리고 다시 해석 모드로 돌아가거나 재컴파일하는 과정

  • 상태 표시
    • made not entrant: 최적화 가정이 깨져 더 이상 사용되지 않는 코드
    • made zombie: 메모리에서 완전히 회수될 준비가 된 코드

6. 주요 최적화 기법

  • 메소드 인라이닝 : 호출되는 메서드의 본문을 호출부에 직접 삽입해 메서드 호출 오버헤드를 없애는 가장 중요한 최적화
  • 탈출 분석(Escape Analysis) : 객체가 특정 메서드 밖으로 나가지 않음을 분석해 Heap 할당 대신 스택에 할당하거나 동기화 락을 제거함

5장 예습

  1. 가비지 컬렉션의 기본 원리: JVM은 어떻게 더 이상 사용되지 않는 객체를 찾고 메모리를 회수할까요? (참조 횟수가 아닌 **‘GC 루트’**로부터의 도달 가능성 분석).

  2. 세대별 가설 (Generational Hypothesis): “대부분의 객체는 금방 죽는다”는 가설에 따라 힙을 Young GenerationOld Generation으로 나누는 이유를 파악해 오세요.

  3. Stop-the-World: GC가 실행될 때 모든 애플리케이션 스레드가 멈추는 현상의 의미와 성능에 미치는 영향을 고민해 보세요.

  4. 주요 알고리즘 명칭: Serial, Throughput(Parallel), G1 GC의 이름과 각각 어떤 상황에서 유리할지 가볍게 훑어보시기 바랍니다.

4장 알아두기

  • C1은 속도, C2는 효율.
  • 코드 캐시가 부족하면 컴파일이 멈춘다.
  • 인라이닝은 메서드 호출 비용을 줄이는 핵심이다.
  • OSR은 긴 루프 도중에 코드를 갈아 끼우는 기술이다.

5장 GC

이번 장은 자바 메모리 관리의 핵심인 GC의 기본 원리와 주요 아록리즘 그리고 기본적인 튜닝 방법을 다룬다

1. GC의 기본 원리

자바의 메모리 관리

개발자가 직접 메모리를 해제할 필요 없이 JVM이 자동으로 불필요한 객체를 찾아 해제한다 C언어의 경우 메모리 할당과 수거를 해줘야함

가비지 판단 기준

GC 루트라 불리는 객체(스레드 스택, 시스템 클래스 등)로부터 도달 가능한지 여부를 확인

힙 압착(Heap Compaction)

메모리 파편화(Fragmentation)를 방지하기 위해 사용 중인 객체들을 한 곳으로 모아 빈 공간을 확보하는 과정

Stop-the-World

GC를 수행하기 위해 모든 애플리케이션 스레드를 멈추는 현상을 의미하며 성능 튜닝의 주요 대상

2. 세대별 가설

핵심: 대부분의 객체는 금방 죽는다는 가설에 기반해 Heap을 두 영역으로 나눔

Young Generation

새 객체가 할당되는 영역으로 Eden과 Survior Spaces(생존자 공간)로 구성됨

Old Generation

Young Generation에서 오랫동안 살아남은 객체들이 이동하는 영역

Minor GC vs Full GC

Young Generation을 정리하는 것은 Minor GC(짧은 정지) 전체 Heap을 정리하는 것은 Full GC(긴 정지)

3. 주요 GC 알고리즘 특징

Serial GC

단일 스레드로 GC를 수행하며 단일 CPU 환겨잉나 매우 작은 Heap(100MB 이하)에 적합

처리량(Throughput/Parallel) GC

여러 스레드를 사용해 정지 시간을 줄이고 처리량을 극대화하며 Java8의 기본값임

현재 11, 17, 21버전의 기본값은?

G1 GC

Heap을 지역(Region)으로 나누어 관리하며 Java11 이상의 기본값으로 짧은 정지 시간을 지향함

CMS GC

Old Generation을 애플리케이션과 동시에 처리해 정지 시간을 최소화하지만 현재는 사용 중단됨

4. 기본 GC 튜닝 플래그

Heap 크기 설정

-Xms(초기크기), -Xmx(최대 크기)로 설정하며 물리 메모리 범위를 넘지 않아야함

세대 비율 조정

-XX:NewRatio=N을 통해 Old와 Young의 비율을 설정(기본값 2)

메타스페이스

클래스 메타 데이터를 저장하는 영역 -XX:MaxMetaspaceSize=N으로 제한 가능

병렬 스레드 수

-XX:ParallelGCThreads=N으로 GC에 사용할 CPU 스레드 수를 제어

5. GC 모니터링 도구

GC 로그 활성화

Java 11에서는 -Xlog:gc * 을 사용해 파일로 저장하고 분석할 것을 권장

jstat

-gcutil 옵션을 통해 실시간으로 각 세대의 사용량과 GC 횟수를 숫자로 확인 가능

jconsole

Heap 사용량을 시각적인 그래프로 모니터링할 때 유용

6장 예습 포인트

  1. G1 GC의 상세 단계: 단순히 정지 시간이 짧은 이유를 넘어, ‘혼합 컬렉션(Mixed GC)‘과 ‘동시 마킹 사이클’이 어떻게 작동하는지 파악하세요.
  2. CMS의 한계: 왜 CMS GC가 G1 GC에 밀려 Deprecated(사용 중단) 되었는지, 특히 ‘메모리 파편화’ 문제와 관련하여 조사해 보세요.
  3. 실험적 컬렉터 (ZGC & Shenandoah): Java 11 이후 등장한 ‘동시 압축(Concurrent Compaction)’ 기능이 무엇이며, 이를 통해 정지 시간을 어떻게 10ms 이하로 줄이는지 관심을 가져 보세요.
  4. 험용 객체(Humongous Objects): G1 GC에서 아주 큰 객체가 할당될 때 발생하는 문제와 이를 처리하는 방식을 예습하세요.

5장 암기 포인트

  • Java 8 기본 GC: Throughput(Parallel) GC, Java 11 기본 GC: G1 GC.
  • 세대별 할당 기준: Eden에 먼저 생성, 살아남으면 Survivor, 더 오래 살면 Old로 이동.
  • 힙 크기 규칙: 물리 메모리보다 크게 설정하면 스와핑(Swapping)으로 인해 성능이 심각하게 저하됨.

6장 가비지 컬렉션 알고리즘

1. 처리량(Throughput) 수집기

  • 목적 : 애플리케이션의 전체 처리량 극대화.
  • 정의 : 여러 스레드를 사용하여 영 세대와 구 세대를 병렬로 수집, 수집 중 애플리케이션 스레드를 정지시키는 수집기

구성 / 분류

  • 영 컬렉션(Minor GC).
  • 전체 컬렉션(Full GC).

핵심 메커니즘

  • 에덴(Eden) 영역이 가득 차면 영 컬렉션을 트리거함
  • 수집 중 모든 애플리케이션 스레드를 정지시킴(Stop-the-world)
  • 단일 GC 사이클 내에서 마킹, 스윕, 압착(Compaction)을 모두 수행함

특징 및 수치, 옵션

  • Java 8 버전의 2개 이상 CPU를 가진 64비트 JVM에서 기본값
  • 활성화 플래그 : -XX:+UseParallelGC
  • -XX:MaxGCPauseMillis=N : 최대 정지 시간 목표 설정
  • -XX:GCTimeRatio=N: GC 시간 비율 설정(기본값 99, 1% 시간 목표)

적용 환경 / 주의점

  • 응답 시간보다 전체 작업 처리량이 중요한 배치성 작업에 적합
  • 개별 작업이 매우 긴 정지 시간에 노출될 수 있음

교재 참고(  p. 153 ~ p. 160, p. 457)

2. G1(Garbage First) 수집기

  • 목적 : 대용량 Heap에서 짧고 예측 가능한 정지 시간 유지.
  • 정의 : Heap을 고정 크기의 지역(Region)으로 나누고, 가비지가 가장 많은 지역을 우선 수집하는 동시 수집기.

구성 및 분류

  • 영 컬렉션(Young Collection)
  • 동시 마킹 사이클(Concurrent Marking Cycle)
  • 혼합 컬렉션(Mixed Collection). ◦ 전체 컬렉션(Full GC)

핵심 메커니즘

  • 힙을 약 2,048개의 지역으로 분할하여 관리함
  • 백그라운드 스레드가 구 세대 객체의 생존 여부를 애플리케이션과 동시에 마킹함
  • 혼합 컬렉션 시 영 세대와 효율이 높은 구 세대 지역을 함께 수집하여 압착함

특징 및 수치, 옵션

  • Java 11 버전 이상의 기본 수집기임
  • 활성화 플래그 : -XX:+UseG1GC
  • -XX:MaxGCPauseMillis=200 : 기본 정지 시간 목표(200ms)
  • -XX:InitiatingHeapOccupancyPercent=45 : 동시 사이클 시작 기준 힙 점유율
  • -XX:G1HeapRegionSize=N : 지역 크기 설정(1MB~32MB, 2의 거듭제곱)

적용 환경 및 주의점

 - 2개 이상의 CPU를 가진 대규모 힙 환경에 적합함  - 동시 모드 실패 또는 대피 실패 시 Java 8에서는 단일 스레드 Full GC가 발생함

교재 참고( p. 160 ~ p. 174, p. 459)

3. CMS(Concurrent Mark-Sweep) 수집기

  • 목적 : 구 세대 수집 시 발생하는 정지 시간 최소화
  • 정의 : 애플리케이션 스레드와 동시에 구 세대를 스캔하여 수집하는 방식

구성 및 분류

  • 초기 마킹, 마킹, 사전 청소, 재마킹, 스윕, 리셋 단계

핵심 메커니즘

  • 초기 마킹과 재마킹 단계에서만 애플리케이션 스레드를 정지시킴
  • 기본적으로 동시 사이클 중에 힙 압착을 수행하지 않음.

특징 및 수치, 옵션

 - Java 11에서 사용 중단(Deprecated)됨  - 활성화 플래그: -XX:+UseConcMarkSweepGC  - -XX:CMSInitiatingOccupancyFraction=70 : 수집 시작 기준 구 세대 점유율

적용 환경 및 주의점

  • 메모리 파편화로 인한 대피 실패 시 매우 긴 단일 스레드 Full GC가 발생
  • Metaspace를 기본적으로 수집하지 않음

교재 참고(p. 174 ~ p. 182, p. 462)

4. TLAB (Thread-Local Allocation Buffer)

  • 목적 : 에덴 영역 내 객체 할당 시 동기화 경합 제거
  • 정의 : 각 스레드에 할당된 전용 객체 할당 버퍼.

핵심 메커니즘

  • 스레드가 잠금 없이 자신의 버퍼에서 객체를 즉시 할당
  • TLAB이 가득 차거나 객체가 너무 크면 힙에 직접 할당

특징 및 수치, 옵션

  • 기본적으로 활성화상태임
  • -XX:TLABSize=N : 초기 크기 설정
  • -XX:-ResizeTLAB : TLAB 크기 조정 비활성화

적용 환경 및 주의점

  • 다중 스레드 환경의 할당 성능 향상에 필수적임
  • 매우 큰 객체를 빈번히 생성할 경우 TLAB 효율이 저하됨

교재 참고(p. 187 ~ p. 191)

5. 실험적 수집기 (ZGC, Shenandoah, Epsilon)

  • 목적 : 극단적인 저지연(10ms 이하) 또는 특수 목적 환경 지원
  • 정의 : 동시 압축을 지원하거나 컬렉션을 수행하지 않는 실험 단계의 수집기

구성 및 분류

  • ZGC : Java 11부터 도입된 저지연 수집기
  • Shenandoah : 동시 압축을 지원하는 저지연 수집기
  • Epsilon : 가비지 수집을 전혀 하지 않는 수집기

핵심 메커니즘

 - ZGC/Shenandoah: 객체 이동 중에도 애플리케이션 접근을 허용하는 배리어 기술 사용  - Epsilon: 메모리 할당만 수행하며 한도 초과 시 종료됨

특징 및 수치, 옵션

 - -XX:+UnlockExperimentalVMOptions 플래그 필요  - -XX:+UseZGC, -XX:+UseShenandoahGC, -XX:+UseEpsilonGC

적용 환경 및 주의점

  • 초저지연 응답이 필요한 시스템(ZGC/Shenandoah) 또는 메모리 수집이 불필요한 단기 실행 프로그램(Epsilon)

교재 참고(p. 197 ~ p. 201)


7장 Heap 메모리 베스트 프렉티스

1. 힙 분석 도구 (Heap Analysis Tools)

  • 목적 : 메모리를 점유하는 객체 및 클래스 식별
  • 정의 : 힙 내 객체 수와 크기를 파악하는 분석 방법

구성 및 분류

  • 힙 히스토그램 (Heap Histograms)
  • 힙 덤프 (Heap Dumps)

핵심 메커니즘

  • 히스토그램은 라이브 객체 통계 추출 시 풀 GC를 유발
  • 힙 덤프는 특정 시점의 전체 객체 그래프 스냅샷을 생성
  • 분석 도구는 도달 가능한 경로를 추적하여 GC 루트(GC Roots)를 식별

특징 및 수치, 옵션

  • jcmd <pid> GC.class_histogram: 히스토그램 출력 명령
  • jcmd <pid> GC.heap_dump <file>: 덤프 파일 생성 명령
  • 얕은 크기(Shallow size): 객체 자체의 메모리 크기
  • 깊은 크기(Deep size): 객체가 참조하는 대상까지 포함한 크기
  • 유지된 크기(Retained size): 객체 해제 시 회수되는 전체 크기

적용 환경 및 주의점

  • 측정 중인 프로그램의 성능에 영향을 주므로 운영 중 사용 시 주의 필요함

교재 참고(p.203 ~ p.209)

2. 메모리 사용량 감소 (Using Less Memory)

  • 목적 : 객체 수와 크기를 줄여 가비지 컬렉션 효율성 증대
  • 정의 : 불필요한 필드를 제거하거나 생성 시점을 늦추는 프로그래밍 기법

구성 및 분류

  • 객체 크기 감소 (Reducing Object Size)
  • 지연 초기화 (Lazy Initialization)
  • 불변 및 캐노니컬 객체 (Immutable and Canonical Objects)

핵심 메커니즘

  • 기본형 변수 사용 시 필요 최소 크기의 타입을 선택함
  • 실제 필드 접근 시점에 인스턴스를 생성하여 초기 오버헤드를 회피함
  • 동일 값을 가진 객체는 단일 인스턴스(Canonical version)를 공유함

특징 및 수치, 옵션

  • 객체 크기는 8바이트 경계로 정렬됨 (Padding)
  • String.intern() : 문자열 캐노니컬화 수행 메서드

적용 환경 / 주의점

  • 지연 초기화는 스레드 안전성을 위해 추가 동기화 비용이 발생할 수 있음

교재 참고(p.215 ~ p.225)

3. 특수 참조 형태 및 객체 재사용 (Object Life-Cycle Management)

  • 목적 : 초기화 비용이 큰 객체의 효율적 재활용 및 자동 해제 제어
  • 정의 : 강한 참조 외에 가비지 컬렉터가 수집 여부를 결정할 수 있는 참조 사용 방식

구성 및 분류

  • 소프트 참조 (SoftReference)
  • 약한 참조 (WeakReference)
  • 팬텀 참조 (PhantomReference)
  • 파이널라이저 (Finalizers)

핵심 메커니즘

  • 소프트 참조는 메모리가 부족할 때 가비지 컬렉터에 의해 회수됨
  • 약한 참조는 다음 가비지 컬렉션 사이클에서 무조건 회수됨
  • 파이널라이저는 객체 회수 전 finalize() 메서드 호출을 대기열에 등록함

특징 및 수치, 옵션

  • -XX:SoftRefLRUPolicyMSPerMB=N : 소프트 참조 생존 시간 제어 (기본값 1000)
  • 파이널라이저 대신 java.lang.ref.Cleaner 사용 권장됨

적용 환경 및 주의점

  • 과도한 객체 풀링(Pooling)은 힙 내 라이브 객체를 유지시켜 GC 속도를 늦춤
  • 파이널라이저는 실행 시점을 보장할 수 없으며 성능 저하를 유발함

교재 참고(p.225 ~ p.246)

4. 압축 Oops (Compressed Oops)

  • 목적 : 64비트 JVM에서 참조형 변수가 점유하는 메모리 오버헤드 완화
  • 정의 : 32비트 오프셋을 사용하여 64비트 주소 체계를 시뮬레이션하는 기술

**핵심 메커니즘

 - JVM은 객체 주소를 8바이트 경계로 정렬함  - 주소값의 하위 3비트(모두 0)를 버리고 32비트로 인코딩하여 저장함  - 레지스터 로드 시 다시 3비트 왼쪽 시프트하여 복원함

특징 및 수치, 옵션

  • 최대 32GB의 힙 메모리까지 참조 가능
  • -XX:+UseCompressedOops: 압축 Oops 활성화 (32GB 미만 Heap에서 기본값)

적용 환경 / 주의점

  • 힙 크기가 32GB를 초과하면 압축 Oops가 비활성화되어 메모리 효율이 급감

교재 참고( p.246 ~ p.248)

5. Out-of-Memory 에러 (Out-of-Memory Errors)

  • 목적 : JVM의 가용한 자원 고갈 상태 진단

구성및 분류

  • Native memory: 네이티브 메모리 고갈
  • Metaspace: 클래스 메타데이터 공간 고갈
  • Java heap space: 자바 Heap 공간 부족
  • GC overhead limit exceeded: GC 효율성 한계 도달

핵심 메커니즘

  • GC overhead limit exceeded는 실행 시간의 98%를 GC에 쓰고 힙의 2% 미만을 회수할 때 발생함
  • 위 조건이 연속 5회 발생 시 에러를 투척함

특징및 수치, 옵션

  • -XX:+HeapDumpOnOutOfMemoryError : 에러 발생 시 자동 힙 덤프 생성
  • -XX:GCTimeLimit=N : GC 시간 제한 비율 설정
  • -XX:HeapFreeLimit=N : 최소 회수량 기준 설정

교재 참고(p.210 ~ p.215)


8장 네이티브 메모리 베스트 프랙티스

1. Footprint

  • 목적 : JVM 프로세스가 점유하는 전체 메모리 사용량 파악 및 관리
  • 정의 : 자바 Heap과 JVM 자체 운영을 위해 사용하는 네이티브 메모리의 총합

구성 및 분류

  • Java Heap
  • Thread Stacks
  • Code Cache
  • Native Library Allocations(네이티브 라이브러리 할당분)

핵심 메커니즘

  • OS는 가상 메모리를 통해 물리 메모리를 관리함
  • JVM은 최대 가용 범위를 예약하고 실제 사용 시점에 메모리를 할당함

특징 및 수치, 옵션

  • 예약 메모리는 주소 공간의 확보를 의미하며 실제 물리 메모리 소비와는 다름
  • 커밋 메모리는 실제 물리 메모리에 할당된 크기를 나타냄
  • 64bit JVM의 스레드 스택 기본값은 보통 1MB임

적용 환경 및 주의점

  • 총 풋프린트가 물리 메모리를 초과하면 swapping이 발생해 성능이 저하됨
  • 32bit JVM은 프로세스 크기가 4GB로 제한되어 예약 메모리 설정에 주의해야함

교재 참고(p.249 ~ p.251)

2. Native Memory Tracking(NMT: 네이티브 메모리 추적)

  • 목적 : JVM 내부 데이터 구조에 의한 네이티브 메모리 소비 분석
  • 정의 : JVM 내부의 메모리 할당 상황을 분류별로 추적하고 보고하는 JVM 기능

구성 및 분류

  • 요약(Summary) 모드
  • 상세 모드

핵심 메커니즘

  • jcmd==명령어를 사용해 실시간 메모리 상태를 출력
  • 기준점(baseline)을 설정해 이후의 메모리 증감량을 비교 분석할 수 있음

특징 및 수치, 옵션

  • -XX:NativeMemoryTracking=[off|summary|detail 플래그로 설정
  • -XX:+PrintNMTStatistics 사용시 JVM 종료 시 통계치를 출력

적용 환경 및 주의점

  • 활성화 시 1% 미만의 성능 오버헤드가 발생
  • JVM 외부의 제 3자 원천 네이티브 라이브러리 할당을 추적하지 않음

교재 참고(p.252 ~ p.256)

3. 공유 라이브러리 네이티브 메모리(Shared Library Native Memory)

  • 목적 : JNI 및 특정 표준 클래스 사용 시 발생하는 외부 메모리 관리
  • 정의 : 공유 라이브러리 또는 네이티브 코드가 직접 시스템 호출을 통해 할당하는 메모리

구성 및 분류

  • 인플레이터 및 디플레이터(Inflater/Deflater)
  • 직접 바이트 버퍼(Direct Bytebuffer)

핵심 메커니즘

  • ByteBuffer.allocateDirect( ) 호출 시 Heap 외부의 네이티브 메모리를 직접 할당
  • Inflater 등은 zlib 같은 플랫폼 네이티브 라이브러리를 사용

특징 및 수치, 옵션

  • -XX:MaxDirectMemorySize=N으로 직접 메모리 할당 한도를 설정
  • 기본 한도는 설정된 최대 Heap 크기(-Xmx)와 동일

적용 환경및 주의점

  • 직접 바이트 버퍼는 할당 비용이 크므로 객체 풀링 등을 통한 재사용이 권장됨
  • Inflater.end() 등을 명시적으로 호출하지 않으면 메모리 누수처럼 보일 수 있음

교재 참고(p.256 ~ p.260)

4. 대형 페이지

  • 목적 : 메모리 주소 변환 효율 향상 및 TLB 히트율 최적화
  • 정의 : OS의 표준 페이지 크기보다 더 큰 단위를 사용해 메모리를 할당하는 기법

구성 및 분류

  • 리눅스 Huge Pages
  • 리눅스 투명한 대형 페이지(Transparent Huge Pages, THP)
  • 윈도우 대형 페이지

핵심 메커니즘

  • TLB(Translation Lookaside Buffer) 하드웨어 캐시의 커버리지를 높여 주소 변환 성능을 개선

특징 및 수치, 옵션

  • -XX:+UseLargePages 플래그로 JVM 레벨에서 활성화함
  • 리눅스의 경우 보통 2MB 크기의 페이지를 기본으로 사용

적용 환경 및 주의점

  • OS 차원의 사전 구성 및 권한 설정이 필수
  • THP는 할당 시 지연(Stall)이 발생할 수 있어 정밀한 튜닝 필요

교재 참고(p.261 ~ p.265)


9장 스레딩 및 동기화 성능

1. ThreadPoolExecutor)

  • 목적 : 스레드 생성 비용 절감 및 시스템 자원 스로틀링(Throttiling)
  • 정의 : 작업을 Queue에 제출하고 미리 생성된 스레드 집합이 이를 실행하는 관리 모델

구성 및 분류

  • Core Pool Size(코어 스레드 수)
  • Maximum Pool Size(최대 스레드 수)
  • Task Queue(작업 큐)
    • SynchronousQueue
    • LinkedBlockingQueue
    • ArrayBlockingQueue

핵심 메커니즘

  • 작업 도착 시 코어 스레드 미달이면 새 스레드 생성
  • 코어 스레드 가득 차면 큐에 작업 저장
  • 큐 가득 차면 최대 스레드 수까지 새 스레드 생성
  • 모든 스레드가 바쁘고 큐가 차면 작업 거부 RejectedExecution

특징 및 수치, 옵션

  • 계산 위주 작업은 CPU 개수만큼의 스레드가 최적
  • 외부 리소스(DB 등) 대기 시 CPU 개수보다 많은 스레드 필요
  • 스레드 대기 시간 설정 가능(KeepAliveTime)

적용 환경 및 주의점

  • 큐가 무제한인 경우 최대 스레드 설정이 무시됨
  • 병목 지점(DB 등)에 부하를 추가하면 전체 성능 저하로 이어짐

교재 참고(p.268 ~ p.278)

2. ForkJoinPool

  • 목적 : 분할 정복(Divide-and-Conquer) 알고리즘의 병렬 처리 극대화
  • 정의 : 작업을 작은 단위로 재귀적으로 분할하고 결과를 결합하는 방식의 스레드 풀

핵심 매커니즘

  • Work Stealling : 유휴 스레드가 다른 스레드 큐의 작업을 가져와 실행

    유휴 스레드란?

  • fork() : 작업을 큐에 비동기로 추가
  • join() : 하위 작업의 완료를 대기하며 필요 시 다른 작업 실행

특징및 수치, 옵션

  • 공용 풀(Common Pool) 기본 크기는 CPU 개수와 동일
  • -Djava.util.concurrent.ForkJoinPool.common.parallelism=N으로 공용 풀 크기 조절 가능
  • Leaf 노드 크기(재귀 중단 시점) 튜닝이 성능에 직결됨

적용 환경 및 주의점

  • 작업의 소요 시간이 불균형할 때 일단 스레드 풀보다 효율적
  • 단순 선형 작업에는 태스크 생성 오버헤드로 인해 비효율적일 수 있음

교재 참고(p.278 ~ p.286)

3. 스레드 동기화(Synchronization)

  • 목적 : 다중 스레드 환경에서 데이터 무결성 보장 및 메모리 가시성 확보

구성 및 분류

  • synchronization 키워드(잠금)
  • CAS(Compare and Swap) 기반 원자적 변수
  • volatile 키워드(메모리 장벽)

핵심 메커니즘

  • Amdahl의 법칙 : 병렬 시스템의 속도 향상은 직렬 실행 부분에 의해 제한됨
  • 레지스터 플러싱 : 동기화 지점에서 CPU 레지스터 값을 메인 메모리에 반영
  • 잠금 인플레이션(Lock Inflation) : 경합 발생 시 비경합 잠금이 무거운 잠금으로 변환

    비경합 잠금이 무거운 잠금으로 변환된다란?

특징및 수치, 옵션

  • 비경합 잠금 획득 비용은 수백 나노초 수준
  • 원자적 변수(AtomicLong 등)는 가벼운 경합 상황에서 잠금보다 빠름
  • LongAdder,LongAccumulator는 높은 경합 상황에서 AtomicLong보다 확장성 좋음

적용 환경 및 주의점

  • 과도한 동기화는 레지스터 플러싱 오버헤드를 유발
  • 스레드 로컬 변수를 사용해 동기화 자체를 회피할 수 있음

교재 참고(p.287 ~ p.294)

4. 거짓 공유(False Sharing)

  • 목적 : CPU 캐시 라인 충돌 방지를 통한 성능 저하 억제
  • 정의 : 서로 다른 스레드가 동일한 캐시 라인에 위치한 독립적인 변수를 수정할 때 발생하는 캐시 무효화 현상

핵심 메커니즘

  • 한 코어에서 변수 수정 시 해당 캐시 라인을 소유한 다른 코어의 캐시가 무효화됨
  • 다른 코어는 메인 메모리에서 데이터를 다시 로드해야함

특징및 수치, 옵션

  • @Contended 어노테이션 : 필드 사이에 자동 Padding 삽입
  • -XX:-RestrictContended : 사용자 코드에서 @Contended 사용 허용(기본 값이 true)
  • 변수 사이에 사용되지 않는 필드를 추가해 수동 패딩 가능

    여기서 말하는 Padding이란?

적용 환경및 주의점

  • 다중 코어에서 volatile 변수를 빈번하게 수정하는 루프에서 주로 발생

    volatile 변수란?

  • 패딩 추가 시 객체 크가가 증가해 GC에 영향줄 수 있음

교재 참고(p.294 ~ p.298)

5. JVM 스레드 튜닝 및 모니터링

특징및 수치, 옵션

  • -Xss<N> : 스레드 스택 크기 설정(64bit 기본 1MB)
  • -XX:-UseBiasedLocking : 편향 잠금(Biased Locking) 비활성화(스레드 풀 환경에서 유리)
  • 스레드 우선순위는 OS 스케줄러에 의해 무시될 수 있음

모니터링 도구

jconsole

실시간 스레드 수 및 스택 확인

Java Flight Recorder(JFR)

모니터 잠금 경합 및 블로킹 이벤트 분석

jstack

스레드 덤프 생성 및 블로킹 지점 확인 가능

교재 참고(p.299 ~ p.306)


10장 자바 서버

1. Java NIO(Non-Blocking I/O)

  • 목적 : 적은 수의 스레드로 대량의 클라이언트 연결을 효율적으로 관리
  • 정의 : 데이터가 준비될 때까지 기다리지 않고 이벤트 알림을 통해 작업을 수행하는 I/O 방식

구성 및 분류

  • Selector : OS로부터 I/O 이벤트를 수신
  • Channel : 데이터가 흐르는 통로
  • Buffer : 데이터를 임시로 저장하는 메모리 블럭

핵심 메커니즘

  • 클라이언트 소켓을 Selector에 등록
  • 데이터 읽기가 가능해지면 OS가 Selector에 알림을 보냄
  • 워커 스레드가 해당 데이터를 읽고 처리한 후 응답을 전송

이거 완전히 WebSocket아님??

특징및 수치, 옵션

  • 클라이언트 연결 수와 서버 스레드 수의 1:1 관계를 해제함
  • 스레드 소비를 줄여 시스템 자원 효율성을 높임

적용 환경및 주의점

  • HTTP Keep-alive를 사용하는 다수 클라이언트 환경에 적합
  • 워크 스레드 내에서 블로킹 작업을 수행하면 전체 Selector 성능 저하

교재 참고(p.307 ~ p.309)

2. Server Thread Pools

  • 목적 : 동시 요청을 처리하기 위한 스레드 자원을 제한하고 관리
  • 정의 : 클라이언트 요청을 처리하기 위해 미리 생성된 스레들의 집합

구성및 분류

  • Selector Threads : I/O 이벤트를 감지
  • Worker Threads : 실제 비즈니스 로직을 수행함

핵심 메커니즘

  • Selector가 감지한 요청을 워커 스레드 풀의 가용 스레드에 할당
  • 작업 완료 후 스레드는 다시 풀로 반환

특징및 수치, 옵션

  • CPU 집약적 작업은 가상 CPU 개수만큼의 스레드가 필요
  • 외부 리소스 대기(블로킹) 작업은 CPU 개수보다 많은 스레드가 필요함
  • Helidon 서버의 8 CPU 기준 기본 스레드 수는 32임

적용 환경 및 주의점

  • 시스템 병목 지점(DB 등)에 과도한 스레드 투입하면 전체 성능 저하됨

교재 참고(p.309 ~ p.311)

3. 비동기 REST 서버(Async Rest Servers)

  • 목적 : 비즈니스 로직 처리를 별도 스레드로 분리해 서버 응답성을 높임
  • 정의 : 요청 스레드는 즉시 반환되고 다른 스레드에서 결과를 처리해 응답하는 방식

핵심 메커니즘

  • @Suspended AsyncResponse을 사용해 응답 객체를 대기시킴 요즘 개발은 Spring으로 하는 추세 Web Flux를 주로 사용해 저 기술을 대체
  • 별도의 ThreadPoolExecutor에서 비즈니스 로직을 수행
  • 로직 완료 시 AsyncResponse.resume( )을 호출해 결과를 전송

특징및 수치, 옵션

  • 시스템 부하 시 AsyncResponse.cancel( )을 통해 즉각적인 거절 처리가 가능
  • 클라이언트에 HTTP 503(Service Unanailable) 상태를 반환해 스로틀링을 구현

적용 환경 및 주의점

  • 여러 외부 서비스를 병렬로 호출해야 하는 비즈니스 로직에 적합
  • 단순히 스레드 풀만 분리하는 것은 컨텍스트 스위칭 비용으로 인해 더 느려질 수 있음

교재 참고(p.311 ~ p.314)

4. 비동기 HTTP 클라이언트

  • 목적 : 외부 호출 시 스레드 점유를 최소화하고 병렬 처리를 극대화함

구성 및 분류

  • Java 8 : HttpURLConnection(동기 중심)
  • Java 11 : HttpClient (비동기 지원 기본 탑재)
  • 외부 라이브러리 : Apache, Jetty, Grizzly 커넥터

핵심 메커니즘

  • 요청 송신 후 InvocationCallbac을 등록해 결과를 비동기로 수신
  • NIO 기반 클라이언트는 응답 대기 시 스레드 할당하지 않고 이벤트로 처리

특징및 수치, 옵션

  • http.maxConnections : Java 8의 기본 연결 풀 크기 5
  • jdk.httpclient.connectionPoolSize : Java 11의 HttpClient는 기본적으로 무제한

적용 환경 및 주의점

  • 다수의 외부 Rest API 결과를 취합하는 어그리게이터 서비스에 필수적임
  • 모든 클라이언트 객체는 스레드 safe하므로 정적 필드를 공유해 재사용해야함

교재 참고(p.314 ~ p.321)

5. JSON 처리

  • 목적 : 텍스트 기반 JSON 데이터를 자바 객체 또는 데이터 구조로 변환

구성및 분류

  • Pull Parsers : 스트림을 토큰 단위로 읽음
  • Document Models : JSON을 트리 구조의 객체로 로드
  • POJO(객체 표현) : 특정 자바 클래스에 데이터를 바인딩

핵심 메커니즘

  • ObjectMapper는 리플렉션과 프록시를 통해 클래스 필드에 데이터를 주입
  • JSON-P/JSON-B 표준 API는 스트림 기반 처리를 지원

특징 및 수치, 옵션

  • Jackson 파서가 기본 구현체보다 실행 속도가 빠름
  • 전체 문서를 읽지 않고 필요한 항목만 필터링하면 성능이 90% 이상 향상됨

적용 환경 및 주의점

  • ObjectMapper 객체 생성 비용이 크므로 애플리케이션 전체에서 단일 객체로 공유해야함
  • 대용량 데이터 처리 시 POJO 방식은 과도한 GC 압박을 유발

교재 근거(p.322 ~ p.327)


11장 DB 성능 베스트 프렉티스

1. JDBC 드라이버

  • 목적 : 자바 애플리케이션과 DB간의 통신 수행
  • 정의 : DB 고유 프로토콜을 자바 인터페이스로 구현한 SW 라이브러리

구성및 분류

  • 타입 2 : 네이티브 라이브러리를 사용하는 드라이버
  • 타입 4 : 순수 자바 코드로 작성된 드라이버
  • Thin 모델 : 처리를 DB 서버에 의존
  • Thick 모델 : 처리를 자바 클라이언트로 오프로드

핵심 메커니즘

  • 애플리케이션이 타입 4 드라이버를 사용할 경우 DB 제조사의 유선 프로토콜을 직접 호출

특징 및 수치, 옵션

  • 타입 1(ODBC 브릿지)은 성능 문제로 사용이 권장되지 않음
  • 타입 4는 배포가 용이하며 타입 2와 유사한 성능 제공

적용 환경및 주의점

  • 하드웨어 리소스 상황에 따라 Thin/Thick 모델 중 적합한 것을 선택

교재 참고(p.330 ~ p.332)

2. JDBC 커넥션 풀

  • 목적 : DB 연결 객체의 생성 및 초기화 시간 절약
  • 정의 : 미리 생성된 DB 연결 객체들을 보관하고 관리하는 집합

DB 커넥션 풀이 JDBC 커넥션 풀??

핵심 메커니즘

  • 스레드가 풀에서 커넥션을 획득해 작업하고 종료 시 풀로 반환

특징및 수치, 옵션

  • 기본 권장 크기는 애플리케이션 스레드 수와 1:1 대응

적용 환경및 주의점

  • DB가 병목일 경우 풀 크기를 제한해 부하를 조절
  • 과도한 커넥션은 GC 압박및 DB 리소스 소모를 초래

교재 참고(p.333 ~ p.334)

3. Prepared Statements and Statement Pooling

  • 목적 : 동일한 SQL 반복 실행 시 DB 분석 및 실행 계획 수립 오버헤드 제거
  • 정의 : SQL 실행 정보를 재사용하기 위해 캐싱된 스테이트먼트 객체 관리 모델

핵심 메커니즘

  • 개별 커넥션 객체 내에서 사용된 프리페어드 스테이트먼트를 풀링

특징및 수치, 옵션

  • setMaxStatements(N) 메서드로 풀링할 스테이트먼트 수를 설정
  • 설정값이 0일시 스테이트먼트 풀링이 비활성화됨

적용 환경및 주의점

  • 일회성 쿼리에는 일반 Statement 사용이 유리
  • 큰 스테이트먼트 풀은 힙 메모리 점유율을 높임

교재 참고(p.334 ~ p.336)

4. 트랜잭션 격리 및 락킹

  • 목적 : 데이터 무결성 보호 및 동시성 제어

구성및 분류

  • TRANSACTION_SERIALIZABLE : 모든 데이터 락 발생
  • TRANSACTION_REPEATABLE_READ : 읽은 데이터 락 유지
  • TRANSACTION_READ_COMMITTED : 쓰기 작업 시에만 락 발생
  • TRANSACTION_READ_UNCOMMITTED : 락을 사용하지 않음

핵심 메커니즘

  • 비관적 락킹 : FOR UPDATE 등을 사용해 데이터 접근을 직접 차단함
  • 낙관적 락킹 : 버전 컬럼을 사용해 커밋 시 충돌 여부를 확인

특징 및 수치, 옵션

  • 기본 격리 수준은 DB 마다 다름

적용 환경 및 주의점

  • 격리 수준이 높을수록 성능과 확장성이 저하
  • 낙관적 락 실패 시 OptimisticLockException 발생

교재 참고(p.336 ~ p.345)

5. 결과 집합 처리

  • 목적 : DB에서 클라이언트의 데이터 전송 효율 최적화

핵심 메커니즘

  • setFetchSize(N)을 통해 한 번의 네트워크 요청으로 가져올 행 수를 결정

특징및 수치, 옵션

  • 오라클 드라이버의 기본 페치 사이즈는 10

적용 환경및 주의점

  • 페치 사이즈가 너무 크면 Heap 메모리 소모 급증
  • 너무 작으면 빈번한 네트워크 라운드 트립 발생

교재 참고(p.345 ~ p.347)

6. JPA 최적화

  • 목적 : ORM 사용 시 발생하는 성능 오버헤드 최소화

핵심 메커니즘

  • 바이트 코드 인핸스먼트 : 엔티티 클래스를 수정해 지연 로딩 및 변경 감지 수행
  • Join Fetch : 연관된 엔티티를 단일 쿼리로 한ㅂ넌에 조회 N+1 문제 해결

특징및 수치, 옵션

  • FetchType.LAZY : 필드 접근 시점에 데이터 로드
  • FetchType.EAGER : 상위 엔티티 로드 시점에 즉시 로드

적용 환경및 주의점

  • 대량의 데이터 처리 시 eclipselink.jdbc.batch-writing과 같은 배치 옵션 활용 필요

교재 참고(p.347 ~ p.353)

7. JPA 캐싱

  • 목적 : DB 재조회 방지를 통한 응답 속도 향상

구성및 분류

  • L1 캐시 : 엔티티 매니저 단위의 트랜잭션 내 캐시
  • L2 캐시 : 애플리케이션 단위의 전역 공유 캐시

핵심 메커니즘

  • 기본키(find( )) 조회 또는 연관 관계 탐색 시 L2 캐시가 작동
  • 일반 JPQL 쿼리는 기본적으로 L2 캐시를 거치지 않고 직접 DB를 호출

특징및 수치, 옵션

  • JOIN FETCH 사용 시 조회 결과가 L2 캐시를 우회할 수 있음

교재 참고(p.353 ~ p.359)


12장 Java SE API 팁

1. Compact Strings

  • 목적 : String 객체의 Heap 메모리 점유량 감소
  • 정의 : 문자열을 16bit 문자 배열 대신 가능한 경우 8bit 바이트 배열로 인코딩하는 방식

핵심 메커니즘

  • 문자열 내용에 따라 8bit 바이트 또는 16bit 문자로 선택적 인코딩 수행

특징 및 수치, 옵션

  • 기본 활성 상태임
  • 활성화 플래그 : -XX:+CompactStrings
  • Java 11부터 도입

적용 환경 및 주의점

  • 16bit 인코딩이 필수적인 문자열 처리 시 성능이 미세하게 저하될 수 있음

교재 참고 (p.363 ~ p.364)

2. String Interning

  • 목적 : Heap 내 중복된 문자열 객체 제거 및 메모리 절약
  • 정의 : 문자열의 유일한 참조본(캐노니컬 버전)을 보관하는 고정 크기 네이티브 해시 테이블 관리 기법

핵심 메커니즘

  • String.intern( ) 메서드 호출 시 네이티브 테이블에서 해당 문자열의 참조를 반환
  • 테이블 내 키값은 약한 참조처럼 동작해 GC 대상이 됨

특징 및 수치, 옵션

  • 테이블 크기 설정 : -XX:StringTableSize=N
  • 기본값 : Java 8(60013), Java 11(65536), 32bit Windows(1009)
  • 통계 출력 플래그 : -XX:+PrintStringTableStatistics

적용 환경및 주의점

  • 테이블 크기가 저장된 문자열 수보다 작으면 충돌 증가로 성능이 저하됨

교재 참고(p.364 ~ p.371)

3. String Concatenation

  • 목적 : 문자열 결합 연산의 런타임 최적화
  • 정의 : 컴파일러와 JVM이 협력해 문자열 병합 코드를 최적의 바이트코드로 변환하는 기능

핵심 메커니즘

  • JDK 8 : StringBuilder 호출 코드로 변환 후 JVM이 내부 최적화 수행
  • JDK 11 : 동적 메서드 호출을 통해 런타임에 직접 최적화된 결합 수행

특징및 수치, 옵션

  • 최적화 활성화 플래그 : -XX:+OptimizeStringConcat(기본값 true)

적용 환경및 주의점

  • 루프 내부에서의 문자열 연결은 매번 객체를 생성하므로 금지
  • 루프 내부 연산에는 반드시 StringBuilder를 명시적으로 사용

교재 참고(p.371 ~ p.374)

4. 버퍼링된 I/O(Buffered I/O)

  • 목적 : I/O 작업 시 빈번한 시스템 호출 방지 및 실행 속도 향상
  • 정의 : 데이터를 블록 단위로 처리하기 위해 하부 스트림을 래핑하는 필터 스트림 사용 기법

구성 및 분류

  • 이진 데이터용 : BufferedInputStream, BufferedOutputStream
  • 문자 데이터 전용 : BufferReader, BufferWriter

핵심 메커니즘

  • 커널 영역으로의 시스템 호출 횟수를 줄여 단일 바이트 처리 오버헤드 제거

적용 환경 및 주의점

  • 압축 스트립(GZIPOutputStream)이나 인코더 사용 시 블록 단위 처리를 위해 버퍼링이 필수적임
  • ByreArrayOutputStream 등 이미 메모리 내에 있는 스트림의 중복 래핑을 불필요

교재 참고(p.374 ~ p.377)

5. 클래스 데이터 공유(CDS)

  • 목적 : 애플리케이션 시작 시간 단축 및 JVM간 메모리 공유
  • 정의 : 클래스 메타데이터를 별도의 아카이브 파일로 생성해 런타임에 빠르게 로드하는 기능

핵심 메커니즘

  • 클래스 로딩 시 파일 시스템 탐색 대신 공유 아카이브를 메모리에 매핑해 즉시 사용

특징 및 수치, 옵션

  • 모드 옵션 : -Xshare:off, -Xshare:on, -Xshare:auto
  • 목록 추출 플래그 : -XX:+DumpLoadedClassList=fileName
  • 아카이브 생성 : java -Xshare:dump

적용 환경 및 주의점

  • JAR 파일이 변경되면 공유 아카이브를 다시 생성해야함
  • 아카이브 생성 시의 클래스 패스는 실행 시 클래스 패스의 부분 집합이어야함

교재 참고(p.377 ~ p.380)

6. 랜덤 번호

구성 / 분류

  • java.util.Random: 동기화된 의사 난수 생성기
  • java.util.concurrent.ThreadLocalRandom: 스레드 전용 난수 생성기
  • java.security.SecureRandom: 시스템 엔트로피 기반 보안 난수 생성기

핵심 메커니즘

  • RandomnextGaussian() 등 호출 시 내부 동기화 락을 사용함
  • SecureRandom은 OS 인터페이스(/dev/random 등)를 통해 시드를 획득함

적용 환경 및 주의점

  • 멀티스레드 환경에서는 락 경합이 없는 ThreadLocalRandom 사용이 권장됨
  • 시스템 엔트로피 부족 시 SecureRandom 호출이 블로킹되어 실행 지연이 발생할 수 있음

교재 참고(p. 381 ~ p. 383)

7. 자바 네이티브 인터페이스 (JNI)

  • 정의 : 자바 바이트코드와 네이티브 C/C++ 코드 간의 상호작용 인터페이스

핵심 메커니즘

  • JNI 경계 횡단 시 주소 변환 및 오버헤드 발생
  • 네이티브 코드에서 자바 배열/문자열 접근 시 객체 고정(Pinning) 수행

적용 환경 및 주의점

  • 잦은 JNI 호출보다 한 번의 호출로 많은 작업을 처리하는 굵은 입자(Coarse-grained) 인터페이스가 유리함
  • 객체가 고정된 동안에는 GC가 실행되지 못해 전체 스레드 정지가 발생할 수 있음

교재 참고(p. 383 ~ p. 386)

8. 예외 처리 (Exceptions)

핵심 메커니즘

  • 예외 생성 시 현재 스택의 호출 이력(Stack Trace)을 캡처함

특징 및 수치, 옵션

  • 스택 캡처 비활성화: -XX:-StackTraceInThrowable
  • JVM은 빈번하게 발생하는 시스템 예외에 대해 스택 트레이스 생성을 생략하도록 자동 최적화함

적용 환경 및 주의점

  • 예외 처리는 단순 제어 흐름용이 아닌 예기치 못한 상황에서만 사용해야 함
  • 스택 깊이가 깊을수록 예외 생성 비용이 증가함

교재 참고(p. 386 ~ p. 390)

9. 자바 컬렉션 API (Java Collections API)

구성 및 분류

  • 동기화 컬렉션: Vector, Hashtable
  • 비동기화 컬렉션: ArrayList, HashMap

핵심 메커니즘

  • 지연 초기화: JDK 7/8부터 첫 데이터 삽입 시점에 내부 배열을 할당함

특징 및 수치, 옵션

  • 내부 배열 크기 확장 시 기존 데이터 복사 오버헤드 발생

적용 환경 및 주의점

  • 데이터 크기를 예측할 수 있는 경우 초기 용량(Initial Capacity)을 명시적으로 설정해야 함

교재 참고(p. 392 ~ p. 397)

10. 객체 직렬화 (Object Serialization)

  • 목적 : 객체의 상태를 이진 데이터로 변환하여 저장하거나 전송함

핵심 메커니즘

  • writeObject()readObject()를 통해 기본 직렬화 로직 재정의 가능

특징 및 수치, 옵션

  • transient 키워드로 특정 필드를 직렬화 대상에서 제외함

적용 환경 및 주의점

  • 중복 참조 유지 시 역직렬화 후 동일 객체 참조 여부가 변하지 않도록 주의해야 함
  • 대량 데이터 전송 시 압축 및 지연 해제(Lazy Decompression)를 고려함

교재 참고(p.402 ~ p.411)