[Java] JVM - 메모리 구조
0. 들어가기 전에
static 변수는 static 영역 메모리에 올라간다. 그래서 GC가 되지 않기 때문에 남용하면 성능 문제를 일으킬 수 있다.
이 문장은 자바와 관련된 내용을 다루는 블로글들을 돌아다니면 한 번쯤 볼 수 있는 문구이다. '문법 공부가 바빠서', '프로젝트가 있어서' 이해가 안되지만 그냥 넘어간적이 한 두 번이 아니다. 그래서 이번엔 이 문장들을 이해하기 위해서 자바의 기초이자 심화과정인 JVM에 대해서 알아보려고 한다.
1. JVM이란?
JVM(Java Virtual Machine)은 자바를 실행하기 위한 가상머신이며, 자바 바이트 코드를 OS에 맞게 해석 해주는 역할을 한다. 또한, 프로그램 메모리를 최적화 해주고 관리한다.
자바는 다른 프로그래밍 언어와는 다르게 OS와 관계없이 어플리케이션을 실행 할 수 있다. 이렇게 동작할 수 있는 이유는 JVM 덕분이다. JVM은 OS 위에서 동작하는 프로세스이면서, JAVAC 컴파일러가 컴파일한 바이트 코드를 해당 OS가 이해 할 수 있는 기계어로 바꿔 실행해준다. 이 때문에 자바 애플리케이션은 OS에 종속되지 않은 채로 실행할 수 있다. 좀 더 자세히 알아보자. (메모리 최적화관련 기능은 2편에서 추가로 다루겠습니다.)
1.1 컴파일
고급 언어로 작성된 소스코드를 컴퓨터가 이해할 수 있도록 기계어로 변환하는 과정
JAVAC 컴파일러가 컴파일을 한다.
여기서 컴파일이란 무엇을 의미할까? 컴파일이란 고급 언어로 작성된 소스코드를 컴퓨터가 이해할 수 있도록 기계어로 변환하는 과정을 의미한다. 자바를 기준으로 컴파일이란 .java
파일을 .class
파일로 변환하는 과정이다.
1.2 바이트 코드
가상 컴퓨터에서 돌아가는 실행 프로그램을 위한 이진 표현식
이 정의는 위키 백과의 바이트 코드의 정의이다. 너무 추상적으로 정의가 되어있기 때문에 쉽게 이해하기가 어렵다. 자바의 관점으로 구체적이게 설명하자면, JVM이 이해할 수 있는 언어로 변환된 자바 소스코드
를 의미한다. 이 자바 바이트 코드들은 .class
파일 형식이다. 이러한 파일들은 기계어가 아니기 때문에 컴퓨터가 이해할 수 없다.
1.3 자바 애플리케이션 동작 과정
따라서 자바 애플리케이션 동작 과정은 다음과 같이 된다.
*.java
코드를 JAVAC 컴파일러가 컴파일해서 바이트코드(*.class
)를 만든다.- JVM이 바이트코드를 각각의 OS가 이해할 수 있도록 명령어로 바꾸어 전달한다.
2. JVM 구조
JVM이 자바 애플리케이션에서 어떤 역할을 하는지 알아봤으니 JVM 구조를 알아보자.
2.1 클래스 로더
클래스 로더는 클래스 파일들을 읽어 메모리에 로드시키는 역할을 하며, 자바 프로그램이 실행되면 해당 프로그램이 클래스로더로 맨 처음 로드 된다.
2.2 메서드(클래스) 영역
메서드 영역은 클래스 영역, static 영역 등으로 불리며 메타데이터와 같은 클래스들의 정의를 저장한다. 또한, 멀티스레드 환경에서 공유 되며, JVM이 종료 될 때 같이 종료된다.
- 저장되는 파일
- 클래스 변수 이름, 타입, 접근 제어자
- 인터페이스
2.3 힙
Garbage Collector가 관리하는 영역으로 어플리케이션에서 생성된 모든 객체들이 저장 된다(객체 정보는 메서드 영역에 저장 됨). 멀티스레드 환경에서 공유 된다. 메모리가 부족하면 OutOfMemoryError
를 발생시킨다.(힙 영역은 좀 더 자세히 나눌수 있는데, GC와 함께 설명하겠습니다.)
2.4 스택
지역변수, 파라미터, 리턴 값 등 임시 값들이 생성되는 영역이다. 메서드 호출 시마다 각각의 스텍 프레임이 생성이 되어 임시 값을 저장한다. 특징은 스레드 별로 각각의 스택이 생성되어 관리된다. 또한, Garbage Collector가 관리하지않고, 메서드가 종료시 자동으로 메모리가 해제된다. 메모리가 부족하면 StackOverFlowError
를 발생시킨다.
2.5 PC Register
자바 가상 머신의 명령어 주소를 가지고 있으며, 스택과 마찬가지로 스레드가 생성될 때 각각 생성 된다.
2.6 Native Method stack
자바 외 언어로 작성된 네이티브 코드를 위한 메모리 영역이다.
2.7 메서드 영역 vs 힙 vs 스택 비교
메서드 영역 | 힙 | 스택 | |
---|---|---|---|
저장 되는 파일 유형 | 클래스 변수(static) 이름, 타입, 접근제어자 | new 키워드로 생성된 객체 | 지역변수, 파라미터, 리턴 값과 같은 임시 값 |
Garbage Collector 관리 | X | O | X (메서드 종료 시 자동 해제) |
멀티 스레드 환경에서 공유 | O | O | X (스레드 별로 생성) |
3. JIT(Java-In-Time Compiler)
자바의 실행 능력을 향상시키도록 도와주는 컴파일러로써 런타임시에 자바 바이트코드를 기계어로 컴파일해준다.
자바는 성능이 느리다
라는 말을 한 번 쯤 들어본 적이 있을 것이다. 자바는 컴파일 언어의 방식(전체 코드를 한 번에 번역 후 실행)과 인터프리터 방식(코드를 한 줄씩 번역 후 실행)을 둘 다 사용하기 때문에 속도가 느리다는게 중론이다.
∵ 자바는 .java
파일을 .class
파일로 컴파일 한 후 JVM의 자바 인터프리터가 한 줄씩 실행하기 때문이다.
그래서 자바는 JIT를 이용해 나름대로 실행 속도를 최적화 하고 있다. JIT는 컴파일러로써, 런타임에 컴파일을 진행한다.
3.1 동작 과정
- JVM이
.class
파일을 생성한 후 JIT에 전달 - JIT가
.class
파일을 네이티브 코드로 컴파일 - OS가 읽고 처리
네이티브 코드는 캐시에 보관되기 때문에 컴파일된 파일들은 인터프리터가 읽지 않아 빠르게 수행할 수 있다. 반면에, 한 번만 실행하는 코드의 경우 인터프리터를 사용하는 것이 속도 면에서 더 빠르다. 그래서 JIT를 사용하는 JVM들은 내부적으로 해당 메서드가 얼마나 자두 수행되는지 체크하고, 일정 정도를 넘을 때에만 컴파일을 수행한다.
※ 참조
'LANGUAGE > [JAVA]' 카테고리의 다른 글
[Java] LinkedHashMap의 방어적 복사 (0) | 2023.03.13 |
---|---|
[Java] JVM - Garbage Collector (2) | 2023.03.12 |
[Java] 롬복의 원리 (0) | 2022.06.08 |
[JAVA] JVM (0) | 2022.06.04 |
정규식이란? (0) | 2022.04.05 |