JVM 겉핥기
- 프로그래밍 언어/자바
- 2025. 1. 21. 18:35
java를 학습하게 되면 어떤것이 중요할까?
객체지향도 중요하지만, java가 어떤것인지도 굉장히 중요하다.
그니까 java가 어떤식으로 코드를 읽는지 그런것도 중요하다는거다.
물론 나는 "코딩만 하고 살거야"(취미면) 그러면 이 내용은 중요하지 않을 수 있다.
하지만 개발자로 일할려면 이 정도는 깊게는 아니어도 어느 정도는 이해도가 있어야 된다.
그래야 java가 특정한 문제가 발생하였을때 쉽게 접근 할 수 있다고 생각한다.
예를 들어 메모리 문제가 발생하면 코드를 수정하는것도 좋지만 jvm의 메모리 사양을 변경시키는 것만으로도 충분히 해결 할 수 있다.
그러면 몇시간 동안 해결해야될일을 5초면 해결 할 수 있다.
이 내용을 모르면 인터넷에 검색하고 그걸 반영해야 할거다. 하지만 이거에 대해 이해를 하지 못한다면 인터넷에 나온 글들은 그냥 종이 쓰레기라 생각한다. 인터넷에는 how 즉 어떻게 이걸 사용하지라는건 굉장히 많이 나왔다. 하지만 when(언제 사용하는지, why(왜 사용하는지)가 중요해졌다고 생각한다. 암튼 서론이 길어 졌는데 암튼 이 글에는 그 흔해빠진 그림을 없을거다. 글만 작성할거다. 애초에 나만 이해하면 되는거라 생각하기 때문이다. 그거에는 그림보다는 글이 최고라고 생각한다.
도대체 자바는 어떻게 코드를 동작 시키는 걸까?
자바는 어떤 언어인가? 인터프리터 언어인가 컴파일 언어인가?
java는 어디에서든 사용이 가능하다는데 왜 이것이 가능할까?
어떻게 맥에서 짠 코드로 윈도우로 동작을 시킬 수 있는 걸까?
이에 대한 질문에 답할 수 있어야 되지 않을까 생각이 든다.
그러면 본격적으로 글을 작성해보자. 글은 14일 부터 20일까지 공부한 내용을 토대로 작성이 되었습니다.
자바 컴파일러(javac)는 소스 코드(.java)를 바이트코드(.class)로 변환할 때, 운영체제와 무관하게 동일한 바이트코드를 생성합니다. 따라서 동일한 소스 코드로 생성된 클래스 파일은 어떤 운영체제에서 실행되더라도 동일합니다.
그러나 바이트코드를 실행하는 과정(JVM)에서는 운영체제의 차이를 처리해야 합니다. 운영체제마다 네이티브 코드(기계어)로 변환하는 방식이 다르기 때문입니다. 여기서 JVM이 중요한 역할을 합니다. JVM은 클래스 파일(바이트코드)을 각 운영체제의 특성에 맞게 해석하고 실행하는 역할을 하므로, 운영체제와 상관없이 동일한 동작을 보장할 수 있습니다.
JVM은 운영체제마다 다른 "언어(기계어)"를 사용하는 환경에서도 동일한 클래스 파일(바이트코드)을 각 운영체제에 맞게 해석하고 실행합니다. 이는 마치 번역기가 원본 텍스트(클래스 파일)를 여러 나라의 언어(운영체제의 기계어)로 번역하되, 항상 동일한 의미를 전달하는 것과 같습니다.
JVM의 첫 번째 단계는 Class Loader를 통해 클래스 파일을 로드하는 것입니다. Class Loader는 클래스 파일을 메모리에 로드하고 초기화하는 역할을 담당하며, 이 과정은 크게 로딩(Loading), 링킹(Linking), **초기화(Initialization)**의 세 가지 단계로 이루어집니다.
- 로딩(Loading):
- 클래스 파일을 메모리에 로드하는 과정입니다. 이 과정은 다음 세 가지 단계로 이루어집니다:
- Bootstrap Class Loader: 자바 플랫폼의 핵심 API(java.lang 패키지 등)를 로드합니다.
- Extension Class Loader: 자바 확장 라이브러리(JAVA_HOME/lib/ext 디렉터리 등)를 로드합니다.
- System/Application Class Loader: 애플리케이션의 클래스와 외부 라이브러리를 로드합니다.
- 클래스 파일을 메모리에 로드하는 과정입니다. 이 과정은 다음 세 가지 단계로 이루어집니다:
- 링킹(Linking):
- 로드된 클래스 파일의 유효성을 검증하고, 클래스 간의 의존성을 확인하는 과정입니다. 이를 통해 잘못된 클래스 파일을 실행 전에 오류를 방지할 수 있습니다.
- 초기화(Initialization):
- 클래스의 정적 변수와 정적 블록을 초기화하며, 프로그램 실행에 필요한 준비를 마칩니다.
이 과정을 통해 JVM은 자바 프로그램이 안전하고 일관되게 실행될 수 있도록 보장합니다.
Execution Engine
JVM은 Class Loader를 통해 메모리에 로드된 바이트코드(.class 파일)를 실행하기 위해 Execution Engine을 사용합니다. 이 엔진은 바이트코드를 읽고 해석하여 프로그램을 실행하는 핵심 역할을 합니다.
- Interpreter(인터프리터):
- Execution Engine의 첫 번째 단계는 인터프리터입니다. 인터프리터는 바이트코드를 한 줄씩 순차적으로 읽고 실행합니다.
- 이 방식의 장점은 빠른 시작과 정확성입니다. 코드가 실행되자마자 바로 해석되어 실행되며, 클래스 파일을 그대로 실행하므로 정확성이 보장됩니다.
- 단점은 한 줄씩 해석하고 실행하기 때문에 속도가 느릴 수 있다는 점입니다. 특히 반복적으로 실행되는 코드에서는 성능 저하가 발생할 수 있습니다.
- JIT(Just-In-Time) 컴파일러:
- 이를 보완하기 위해 JVM은 JIT(Just-In-Time) 컴파일러를 제공합니다. JIT 컴파일러는 다음과 같이 동작합니다:
- 프로그램 실행 중, 인터프리터가 코드를 해석하며 실행합니다.
- 자주 실행되는 코드가 감지되면, 해당 코드를 네이티브 코드(기계어)로 컴파일하여 저장합니다.
- 이후 해당 코드가 실행될 때는 인터프리터가 아닌 이미 컴파일된 네이티브 코드를 실행하므로, 성능이 크게 향상됩니다.
- 이를 보완하기 위해 JVM은 JIT(Just-In-Time) 컴파일러를 제공합니다. JIT 컴파일러는 다음과 같이 동작합니다:
GC(Garbage Collection)
JVM에서 **GC(Garbage Collection)**는 불필요한 객체를 자동으로 메모리에서 제거하여 메모리를 관리하는 중요한 기능입니다. GC가 발생하는 과정은 마킹(Marking), 스위핑(Sweeping), **컴팩팅(Compact)**의 세 단계로 이루어집니다.
- 마킹(Marking):
- 이 단계에서는 JVM이 살아있는 객체와 죽은 객체를 구분합니다. 살아있는 객체는 여전히 참조되고 있는 객체들로, 이들은 이후 GC의 대상에서 제외됩니다. 반면, 참조되지 않는 객체들은 죽은 객체로 마킹됩니다.
- 스위핑(Sweeping):
- 마킹 단계에서 죽은 객체로 표시된 객체들을 메모리에서 제거합니다. 이 과정에서는 더 이상 사용되지 않는 객체들이 메모리에서 회수되어, 메모리 공간이 해제됩니다.
- 컴팩팅(Compact):
- 스위핑을 통해 메모리에서 객체가 제거된 후, 남아 있는 객체들 사이에 공간이 생깁니다. 이 과정은 메모리의 **단편화(fragmentation)**를 방지하기 위해 남은 객체들을 한 곳으로 밀어넣어 연속적인 메모리 공간을 확보하는 작업을 합니다. 이를 통해 새로운 객체를 할당할 수 있는 충분한 연속된 메모리 공간을 확보하게 됩니다.
GC가 이런 과정을 거치는 이유는 메모리 단편화를 방지하고, 프로그램의 성능을 유지하기 위해서입니다. 단편화는 메모리의 여러 부분에 분산되어 객체가 할당되어, 실제로는 메모리 전체에서 새로운 객체를 할당할 수 없는 상황을 초래할 수 있기 때문에, GC의 컴팩팅 단계는 이를 해결합니다.
키워드
- 자바 컴파일러 (javac)
- 바이트코드 (.class 파일)
- JVM (Java Virtual Machine)
- 운영체제
- Class Loader
- 로딩 (Loading)
- 링킹 (Linking)
- 초기화 (Initialization)
- Execution Engine
- Interpreter (인터프리터)
- JIT (Just-In-Time) 컴파일러
- 네이티브 코드 (기계어)
- GC (Garbage Collection)
- 마킹 (Marking)
- 스위핑 (Sweeping)
- 컴팩팅 (Compacting)
- 단편화 (Fragmentation)
- 메모리 관리
- 객체 생명주기
- 불필요한 객체 제거
- 메모리 공간 확보
'프로그래밍 언어 > 자바' 카테고리의 다른 글
String vs StringBuilder vs StringBuffer (0) | 2025.02.07 |
---|---|
해싱 (1) | 2025.01.29 |
인터페이스 (0) | 2022.03.21 |
SOLID (0) | 2021.09.10 |
LocalDateTime (미 정재 .ver) (0) | 2021.09.04 |