| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
- 자바기초
- 자바프로그래밍
- 코딩인터뷰
- 멀티스레드
- 개발공부
- 프로그래밍기초
- JVM
- 알고리즘공부
- 예외처리
- 자바공부
- 백준
- 코딩테스트팁
- 파이썬
- 객체지향
- 클린코드
- Java
- 가비지컬렉션
- HashMap
- 개발자취업
- 자바개발
- 메모리관리
- 코딩공부
- 코딩테스트
- 코딩테스트준비
- 자바
- 정렬
- 프로그래머스
- 알고리즘
- 자료구조
- 개발자팁
- Today
- Total
코드 한 줄의 기록
Java 컴파일과 실행 과정 완벽 가이드: JVM 작동 원리 이해하기 본문
Java는 “Write Once, Run Anywhere”를 실현한 언어로, 소스 코드를 컴파일하고 JVM이 실행하는 독특한 구조를 갖추고 있습니다. 이 글에서 Java 컴파일러와 JVM의 내부 동작 과정을 단계별로 살펴보고, 개발자가 이해해야 할 핵심 개념과 팁을 자연스럽고 구어체에 가깝게 알려드립니다.
왜 컴파일과 JVM 동작 과정을 알아야 할까?
- 디버깅과 성능 최적화
- 버그가 발생하거나 성능 저하 문제를 마주했을 때, 컴파일러 옵션이나 JVM 설정을 조정하면 문제 해결에 효과적입니다.
- 플랫폼 독립성 이해
- Java의 핵심 강점 중 하나인 플랫폼 독립성(Portability)은 바이트코드(.class 파일)와 JVM 덕분에 나옵니다.
- 최신 Java 기능 활용
- 모듈 시스템, JIT 컴파일러, GC(Garbage Collector) 등 최신 Java 기능의 동작 원리를 알면 프로젝트에 맞는 JVM 튜닝과 코드를 작성할 수 있습니다.
Java 컴파일 단계: 소스에서 바이트코드까지
1. 자바 소스(.java) 파일
개발자가 작성하는 .java 파일에는 클래스, 인터페이스, 메소드, 필드가 정의되어 있습니다.
- 패키지 선언: package com.example;
- 클래스 선언: public class MyApp { … }
2. javac 컴파일러
터미널에서 javac MyApp.java 명령을 실행하면, 자바 컴파일러가 다음 작업을 수행합니다.
- 렉시컬 분석(Lexical Analysis)
소스 코드를 토큰(token)으로 분리 - 구문 분석(Syntax Analysis)
토큰 스트림을 AST(Abstract Syntax Tree)로 변환 - 의미 분석(Semantic Analysis)
타입 검사, 참조 유효성 확인 - 바이트코드 생성(Bytecode Generation)
AST를 기반으로.class파일에 JVM 명령어 형태로 변환
팁: 컴파일 오류가 나면, 오류 메시지의 위치와 내용을 꼼꼼히 확인하고, 빈번한 실수인 세미콜론(;) 빠짐, 타입 불일치 등을 우선 점검하세요.
JVM 내부 구조와 실행 흐름
JVM은 자바 바이트코드를 로딩(load)하고, 해석(interpret)하거나 JIT(Just-In-Time) 컴파일하여 머신 코드로 변환한 뒤 실행합니다. JVM은 크게 클래스로더, 메모리 영역, 실행 엔진으로 구성됩니다.
1. 클래스 로더(Class Loader)
- Bootstrap ClassLoader: JDK 내부 핵심 클래스(
rt.jar) 로드 - Extension ClassLoader: JRE 확장 라이브러리 로드
- Application ClassLoader: 애플리케이션 클래스패스에 있는 클래스 로드
클래스 로더는 계층 구조로 위에서 아래로 클래스를 찾으며, 네이티브 라이브러리(.dll, .so) 로딩도 담당합니다.
2. JVM 메모리 구조
- 메소드(Method) 영역
- 클래스 구조, 상수 풀, 메소드 바이트코드 저장
- 힙(Heap)
- 인스턴스 객체와 배열 저장, 가비지 컬렉션 대상
- 스택(Stack)
- 각 쓰레드별로 호출 스택(Stack Frame) 생성
- 지역 변수, 피연산자 스택, 프레임 데이터 포함
- 프로그램 카운터(PC) 레지스터
- 현재 실행 중인 바이트코드 위치 저장
- 네이티브 메소드 스택(Native Method Stack)
- JNI(Java Native Interface) 호출 시 사용
3. 실행 엔진(Execution Engine)
- 인터프리터: 바이트코드를 한 줄씩 해석
- JIT 컴파일러: 자주 사용하는 바이트코드를 머신 코드로 변환 캐싱
- 가비지 컬렉터(GC): 힙의 사용되지 않는 객체 메모리 해제
팁: 대규모 서비스에서는 GC 튜닝이 핵심입니다. G1, ZGC 등 최신 GC 알고리즘을 프로젝트 특성에 맞게 선택하세요.
실제 실행 흐름: 예시로 살펴보기
# 프로젝트 구조
src/
└─ com/example/MyApp.java
# 컴파일
javac -d out src/com/example/MyApp.java
# 실행
java -cp out com.example.MyApp
javac가out/com/example/MyApp.class생성java명령이 Application ClassLoader에 의해 클래스 로드main()메소드가 호출되어 Stack Frame 생성- 바이트코드가 해석 또는 JIT 컴파일 후 실행
main()종료 → Stack Frame 제거- 프로그램 종료 후 GC 정리, JVM 종료
JVM 옵션과 성능 튜닝
-Xms,-Xmx: 초기/최대 힙 크기 설정-XX:+UseG1GC: G1 GC 활성화-XX:MetaspaceSize,-XX:MaxMetaspaceSize: 메타스페이스 크기 조정-XX:+PrintGCDetails: GC 로그 출력
성능 팁
- 로컬 개발 환경에서 GC 로그를 분석하고, 메모리 사용 패턴에 따라 힙 크기를 조절하세요.
- 클라우드 환경에서는 컨테이너 메모리 한계와 JVM 힙 크기를 조율하는 것이 안정적 운영의 핵심입니다.
Java 9 이후의 모듈 시스템과 JVM
Java 9부터 도입된 모듈 시스템(Jigsaw)은 module-info.java로 모듈 간 의존성을 명시합니다.
장점: 모듈 별 접근 제어, 경량화된 런타임 이미지 생성(jlink)
사용법
// module-info.java
module com.example.myapp {
requires java.sql;
exports com.example.service;
}
실행: java --module-path mods -m com.example.myapp/com.example.Main
모듈 시스템은 대규모 프로젝트에서 클래스 충돌과 의존성 관리를 단순화해 줍니다.
정리 및 실전 팁
Java 컴파일과 JVM 실행 과정은 바이트코드, 클래스로더, 메모리 구조, 실행 엔진 네 부분으로 요약할 수 있습니다.
실무에서 바로 활용할 수 있는 팁:
- 컴파일 최적화: javac 옵션에 -g:none을 추가해 디버그 정보를 제외하면 클래스파일 크기 감소
- JIT 힌트: -XX:CompileThreshold로 JIT 컴파일 기준 변경
- GC 튜닝: 프로덕션 환경에서 적절한 GC 알고리즘 선택과 힙 크기 조절
이 글을 참고해 Java 개발과 운영 과정에서 발생하는 오류와 성능 이슈를 스스로 분석·해결할 수 있길 바랍니다. 여러분의 프로젝트에 Java의 진정한 가치를 더해 보세요!
'JAVA' 카테고리의 다른 글
| Java 식별자, 키워드, 주석, 코드 스타일 기본 규칙 완벽 가이드 (0) | 2025.09.21 |
|---|---|
| 자바 프로젝트 구조 완벽 가이드: 소스에서 바이너리까지 빌드 흐름 쉽게 이해하기 (0) | 2025.09.20 |
| Java 개발자를 위한 완벽한 IDE 선택 가이드: IntelliJ, Eclipse, VS Code 비교와 실전 활용법 (0) | 2025.09.17 |
| 초보자를 위한 Java PATH/JAVA_HOME 설정과 첫 번째 Hello World 실행하기 (Maven/Gradle 없이) (0) | 2025.09.16 |
| 자바 개발 완전정복! JDK/JRE/JVM 개념 차이부터 설치까지 한 번에 끝내기 (0) | 2025.09.15 |