티스토리 뷰

이번 글은 4번 자바 어플리케이션 실행 과정에 대해서 작성한다.

1. JVM 구성 요소와 JVM Warm-up 전략
2. JVM 메모리 구조
3. JVM 가비지 컬렉션
4. 자바 어플리케이션 실행 과정
5. Java 비동기 처리 (프로세스, 쓰레드, 비동기 처리)

 

 

자바 어플리케이션의 실행 과정을 통해 내부에서 JVM이 어떻게 사용되는지 확인할 수 있다.

 

1. 자바 어플리케이션의 실행 과정의 실행 과정 요약

1. 소스코드 → 바이트코드 변환

자바 컴파일러(javac)는 소스코드를 플랫폼 독립적인 바이트코드로 변환한다.

바이트코드는 JVM이 이해할 수 있는 형태로, 다양한 운영 체제에서 동일하게 실행된다.

2. JVM 실행: 클래스 로더가 클래스 파일을 메모리에 동적으로 로드

JVM을 시작하고, JVM은 실행에 필요한 클래스를 동적으로 로드하고, 메서드 영역(Method Area)에 적재한다.

클래스 로더가 .class 클래스 파일을 로드하여 JVM의 메모리에 적재하고, 클래스의 메서드 영역을 초기화한다.

클래스 로딩 과정은 로딩(Loading)링크(Linking)초기화(Initialization)의 단계를 거친다.

3. 런타임 데이터 영역에서 메모리 관리

실행 중 생성된 데이터는 런타임 데이터 영역에 저장된다.

  • 메서드(Method) 영역: 클래스 메타데이터(클래스의 정보, 메서드, 필드 선언)와 같은 정적 데이터를 저장한다.
  • 힙(Heap) 영역: 프로그램 실행 중 생성된 객체를 저장한다.
  • 스택(Stack) 영역: 메서드 호출과 관련된 지역 변수 및 호출 정보를 관리한다.

4. 실행 엔진 작동

실행 엔진은 바이트코드를 인터프리터로 해석하고, JIT 컴파일러로 기계어로 변환하여 실행한다.

JIT 컴파일러는 자주 실행되는 코드인 핫스팟을 기계어로 변환해 캐시에 저장하고, 반복 실행 시 성능을 향상시킨다.

5. GC가 수행되고 메모리 정리

Garbage Collector(GC)는 사용되지 않는 객체를 찾아 힙 메모리에서 제거하여 메모리 누수를 방지한다.

GC의 동작 과정은 마킹, 스위핑, 컴팩팅 과정을 거친다.

  • 마킹(Marking): 참조되지 않은 객체 식별
  • 스위핑(Sweeping): 식별된 객체를 제거
  • 컴팩팅(Compacting): 메모리 단편화를 방지하기 위해 남은 객체를 재배치

 

 

2. 소스코드에서 바이트코드로 변환 (컴파일 단계)

2.1. 소스코드 작성과 javac 컴파일러

자바(.java)로 작성한 소스코드 파일은 사람이 이해할 수 있는 언어로 작성된다.

이 소스코드는 javac 자바 컴파일러에 의해 컴파일 되고, 바이트코드(.class)로 변환된다.

 

바이트코드는 JVM이 이해할 수 있는 중간 언어이다. JVM에는 왜 바이트코드로 변환하는 과정이 있을까?

 

2.2. 바이트코드의 역할

바이트코드는 특정 OS나 플랫폼에 독립적이므로 Windows, macOS, Linux 등 다양한 환경에서 동일하게 실행된다.

이는 JVM이 바이트코드를 컴파일하여 각 플랫폼에 맞는 기계어로 변환하기 때문에 가능하다.

 

 

 

3. JVM 실행: 클래스 로더의 클래스 로딩 및 초기화 과정

3.1. 클래스 로더 서브시스템 (Class Loader SubSystem)

JVM이 실행되면, 필요한 클래스를 "동적"으로 로드하고 메모리에 적재한다.

클래스를 로딩하는 과정은 3단계로 클래스 로더 서브시스템에 의해 이루어진다.

1. 로딩(Loading)

.class 파일(바이트코드)을 읽어 JVM 메모리에 적재한다. 적재된 클래스는 메서드 영역에 저장된다.

2. 링크(Linking)

클래스 파일의 내부 구조를 확인하고 세 단계를 거쳐서 최종적으로 실행 가능한 형태로 변환된다.

(1) 검증: 바이트코드가 유효한지 검증한다. 클래스 파일의 구조와 유효성을 검사하여 올바른 형식인지 확인한다.

(2) 준비: 클래스의 static 필드를 위한 메모리를 할당하고 초기화한다.

(3) 해결: 외부 클래스, 메서드, 필드 참조를 실제로 연결한다.

3. 초기화(Initialization)

클래스가 메모리에 로드되고 링크가 완료되면, 초기화 과정이 시작된다. 이 과정에서는 클래스의 static 블록static 필드가 실행된다.

  • Static 초기화 블록: 클래스가 처음 사용될 때 한 번만 실행되는 코드
  • Static 필드 초기화: 클래스 로딩 시 static 필드가 초기화된다.

 

3.2. 메서드 영역에 저장

로드된 클래스의 메타데이터(클래스의 정보, 메서드, 필드 선언)는 런타임 데이터 영역의 메서드 영역(Method Area)에 저장된다.

이를 통해 JVM은 클래스의 정보를 참조하며 실행 시 필요한 메서드와 필드를 관리한다.

 

 

 

4. 실행 엔진의 역할: 인터프리터와 JIT 컴파일러

4.1. 실행 엔진의 작동 방식

실행 엔진은 메서드 영역에서 바이트코드를 해석하거나, JIT 컴파일을 통해 기계어로 변환한다.

💡 인터프리터(Interpreter)

바이트코드를 한 줄씩 해석하여 기계어로 변환한다.

초기 실행 속도가 빠르지만 반복 실행되는 코드에서는 비효율적이다.

💡 JIT(Just-In-Time) 컴파일러

인터프리터의 단점을 보완해서 자주 실행되는 코드(핫스팟)를 기계어로 변환하여 캐싱한다.

동일한 코드가 반복 실행될 때 재사용하므로 실행 속도가 향상된다.

 

4.2. JIT 컴파일과 Warm-up 전략

JIT 컴파일은 초기 실행 시 Warm-up 단계가 필요하다.

 

JVM은 코드의 실행 빈도를 분석하여 특정 메서드가 자주 실행되는 코드인 핫스팟으로 식별한다. 이렇게 식별된 핫스팟 메서드는 JIT 컴파일러에 의해 최적화되어 기계어로 변환된다. 초기 실행에서는 JVM이 어떤 코드가 자주 실행되는지 알 수 없기 때문에, Warm-up 단계 동안 실행된 코드들이 반복적으로 호출되며 실행 빈도가 높은 코드들이 최적화 대상으로 식별된다.

 

Warm-up 전략은 프로그램의 초기 실행 속도를 높이면서, 반복 실행되는 코드가 최적화될 수 있도록 하는 과정이다. 이 전략을 통해 JIT 컴파일러는 실행 중인 코드에서 성능을 향상시킨다. 즉, Warm-up 과정이 끝난 후 자주 호출되는 메서드가 최적화되어 성능이 개선되고, 초기 실행 속도는 조금 낮지만 장기적으로는 훨씬 더 효율적으로 실행된다.

 

4.3. 핫스팟(HotSpot) 영역

핫스팟은 JVM에서 자주 실행되는 코드 영역이다.

JVM은 코드 실행 중에 반복적으로 호출되거나 빈번하게 실행되는 메서드와 코드 경로를 추적하고 핫스팟으로 식별한다. 핫스팟은 JIT(Just-In-Time) 컴파일러가 최적화를 통해 성능을 향상시키는 대상이 된다.

핫스팟은 JIT 컴파일러에 의해 최적화되어 기계어로 변환되고, 최적화된 코드는 이후 반복적인 실행에서 성능을 극대화한다.

 

JIT 컴파일러의 최적화 기술

JIT 컴파일러는 핫스팟을 식별한 후, 최적화 기법을 통해 성능을 향상시킨다.

1. 인라이닝 (Inlining)

인라이닝은 메서드를 호출하는 코드 대신, 메서드의 바디를 직접 삽입하는 최적화 기법이다.

메서드 호출이 빈번한 경우, 인라이닝을 통해 메서드 호출 비용을 줄이고, 실행 속도를 높일 수 있다. 인라이닝은 특히 간단한 메서드나 자주 호출되는 작은 메서드에서 성능 향상에 효과적이다.

 

2. 루프 최적화 (Loop Optimization)

루프는 프로그램에서 자주 실행되는 코드 블록 중 하나로, JIT 컴파일러는 루프의 실행을 최적화하여 성능을 개선한다.

루프 최적화 기술로는 루프 언롤링(Loop Unrolling), 루프 불변 값 이동(Loop Invariant Code Motion), 루프 카운터 최적화 등이 있다. 이러한 최적화는 루프의 반복 횟수를 줄이거나, 루프 내에서 불필요한 계산을 제거하여 실행 속도를 높인다.

 

3. 메모리 관리 최적화 (Memory Management Optimization)

핫스팟 영역에서는 객체 생성과 메모리 할당이 빈번하게 발생할 수 있다. JIT 컴파일러는 이러한 메모리 할당을 효율적으로 처리하기 위해 메모리 관리 최적화를 수행한다. 예를 들어, 객체 풀링(Object Pooling) 기법을 활용하거나, 불필요한 객체 생성을 피하는 방식으로 메모리 사용을 최적화할 수 있다.

 

4. 간헐적 최적화 (Adaptive Optimization)

JIT 컴파일러는 프로그램 실행 중에 동적으로 최적화를 적용한다. 예를 들어, 자주 호출되지 않는 코드 경로는 초기에는 최적화하지 않고, 실행 빈도가 늘어날수록 점진적으로 최적화한다. 이를 통해 초기 실행 속도는 빠르게 유지하면서도, 장기적으로 자주 호출되는 코드에 대한 최적화를 점진적으로 적용한다.

 

 

 

 

반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함