이 글은 자바 프로그램이 운영체제에 독립적으로 구동되게 해주는 근간이 되는 JVM에 대해 공부하고 정리한 글이며
조금이나마 이해에 도움이 되기를 바란다.
내용이 방대하다고 판단해 여러 파트에 걸쳐 구성한 점 참고바란다.
배경
자바를 처음 공부하기 시작한 때에 실습차원에서 간단한 애플리케이션을 만들던 중에
작성한 클래스와 필드가 메모리 구조상에서 어떻게 save/load 되고 메모리 관리가 되는지,
프로젝트 폴더에 있는 .class 라는 파일이 빌드간에 왜 생성되고 무슨 역할하며 어떻게 실행되는건지 궁금해졌다.
이 모든게 JVM과 연관이 있다는 것을 공부하며 알게됐고
나아가 고성능, 고가용성, 메모리 관리가 되는 코드를 작성하기 위한 선수지식이라는 것을 알게돼 정리하게 됐다.
과정
JVM 이란?
바이트코드(.class 파일)를 OS 에 맞는 기계어로 변환하고,
이를 클래스 로더에서 읽어와 메모리 영역에 저장/관리 하고,
이를 실행엔진에서 바이트코드 명령어 단위로 읽어들여 실행하며
가비지 컬렉터로 메모리 관리를 하는 가상 머신을 말한다.
자바 프로그램은 설치된 컴퓨터의 운영체제에 대해서 독립적이라 말할 수 있다.
컴퓨터에서 프로그램을 직접 실행하는 게 아니라 인터페이스 역할을 하는
자바 가상 머신(Java Virtual Machine)에 의해 실행되기 때문이다.
이것은 자바의 탄생 이유이자 목적이기도 하다.
우선 저 자바 바이트코드부터 알아보자.
바이트코드
바이트코드(Bytecode, portable code, p-code)는 특정 하드웨어가 아닌 가상 컴퓨터에서 돌아가는 실행 프로그램을 위한 이진 표현법이다. 특정 하드웨어에 대한 의존성을 줄이고, 인터프리팅도 쉬운 결과물을 생성하고자 하는 프로그래밍 언어에 의해, 출력 코드의 한 형태로 사용된다. - Wikipedia 정의 -
💡 혹시 이진 표현법이라고 해서 바이너리 코드와 헷갈릴 수 있으니 간략히 정리가 된 글을 링크하겠다.
그런데 바이트코드는 왜 만드는걸까?
아래 그림에 핵심 이유가 나타나 있다.
한 마디로 중간 단계를 하나 둬서 간접화를 통해 경우의 수를 낮추고 효율을 높이기 위해 중간 코드를 생성한다.
중간 코드 역할을 하는 바이트코드의 생성과정을 아주 간단하게 표현하자면 위와 같다.
자바 소스 코드 파일(.java) → javac 컴파일러 → JVM 바이트코드(.class)
💡 자세한 컴파일 과정은 이 글에 잘 정리돼있어서 참고하기 바란다.
이렇게 경우의 수를 낮추고 효율을 높이기 위해 만들어진 바이트코드 또한
결국에는 기계가 이해하기 위해 기계어로 번역이 돼야 한다.
이때 인터프리터를 사용하여 프로그램이 실행 중인 런타임 시 한 줄씩 읽기 때문에
런타임 전 소스코드를 미리 읽어서 기계어로 변환하는 방식의 컴파일러보다는 느릴 수밖에 없다.
이런 문제를 개선해 주는 것이 JIT 컴파일러이다.
JIT 컴파일러에 대해 더 알고 싶으시다면 따로 정리해뒀으니 참고바란다.
다시 JVM을 공부하게 된 배경으로 돌아가서...
JVM은 바이트코드를 어떻게 실행할까?
JVM이 어디에서 어떻게 동작하고 있는지 보면서 공부해보자.
JDK
자바 개발 환경(Java Development Kit)
컴파일러(javac), 역어셈블러(javap), 디버거(jdb), 의존관계 분석(jdeps) 등 개발에 필요한 도구를 제공한다.
JRE
자바 실행 환경(Java Runtime Enviornment)
자바 실행 명령, 프로그램 실행 실패 등이 발생할 경우 대화 상자를 표시하는 시작 프로그램 파일(javaw.exe),
클래스로더와 바이트코드의 실행(java.exe)에 필요한 기본 라이브러리(rt.jar)를 제공한다.
💡 rt는 RunTime를 의미한다. rt.jar는 자바 런타임 환경에서의 코어 자바 클래스 컬렉션이다.
JVM에서 rt.jar파일에 담긴 클래스 파일들을 런타임시에 메모리에 올린다.
아무렇지 않게 사용했던 String 클래스, System 클래스 같은 클래스가 rt.jar 안에 속해 있다.
💡 단, rt.jar는 Java 9부터 삭제됐는데
그 이유는 선택적인 사용과 메모리 로딩이 안되는 기존 모노리틱(Monolithic) 방식을 개선하여
모듈 지정 및 모듈별 버전 관리 기능이 추가됐고
필요한 모듈만 구동해서 크기와 성능 최적화가 가능해졌기 때문이다.
JVM
자바 가상 머신(Java Virtual Machine)
바이트코드 인터프리터, JIT 컴파일러, 링커, 명령어 세트, 가비지 컬렉터, 런타임 데이터 영역(메모리) 등
OS에 독립적으로 실행될 수 있는 추상층을 제공한다.
💡 가비지 컬렉터도 별도로 다룰 정도로 중요하므로 다른 분이 잘 정리하신 글에 링크를 걸어둔다.
또한 가비지 컬렉터에 대해 아래 두개의 글로 다루었으니 참고 바란다.
이러한 구조속에서 JVM의 바이트코드를 실행하는 과정을 간단히 보자면
JDK를 사용해서 바이트코드(.class 파일)를 만들고
JRE를 사용해서 바이트코드를 실행하게 요청하면
JVM이 구동되면서 바이트코드의 실질적인 실행(실제 OS에 메모리 할당/회수, 시스템 명령 호출 등 요청)을 하게 된다.
지금까지의 내용을 간략화하면 아래와 같다.
바이트코드는 특정 하드웨어에 대해 의존성을 줄이고 이식성을 높이기 위해 만들어진 것이며,
자바에서는 JDK 내 자바 컴파일러가 소스코드에 대한 컴파일을 성공적으로 마치면
자바 바이트코드(.class 파일)를 생성해 JRE에게 제공하고
JRE는 JVM에게 바이트코드를 실행하게 한다.
결과
JVM과 관련하여 아래 사항들을 공부하고 정리했다.
- 바이트코드 정의
- 바이트코드 사용 이유
- JVM이 어떤 구조 속에서 바이트코드를 실행하는지에 대한 과정
성과
- 이 글은 2020년 12월, 2021년 2월, 3월 총 3번에 걸쳐 정리했는데코드 뿐만 아니라 글도 설명하고자 하는 범위와 역할을 적절히 분리하는게 중요하다는 것을 알게된 계기가 됐다.
- 이 주제에 대한 공부는 백기선님 자바 라이브 스터디 에서 시작했으며, 꾸준히 공부하는 습관을 들이고 여러 사람들과 공부내용을 공유하며 시간대비 알찬 내용을 얻고 사고방식도 배울 수 있는 가장 효율적인 공부방식이 스터디라는 것을 직접 느꼈다. 그래서 앞으로도 주제와 상관없이 스터디를 적극 활용해야 겠다고 생각한 계기가 됐다.
JVM을 이해하면 활용할 수 있는 게 무엇이 있는지 궁금해서 찾아봤더니
자바 애플리케이션 성능 튜닝에 있어서 기본이 된다고 한다.
Garbage Collection(GC)에 대한 것과 HotSpot에 대한 것, JVM 구조와 동작 과정, OS가 각 프로세스에 자원을 어떻게 분배하고 관리하는가, 클래스 로딩, 객체 생성, Lock과 Concurrency 등을 중심으로 부가적인 지식과 함께 종합적으로 고려하여 성능 튜닝을 할 수 있는 것이다.
추후 성능 튜닝에 대해 공부해야 하는 시점이 온다면 다시 이 글을 보며 공부한 내용을 추가로 정리해야겠다.
내 공부에도 여러분들의 공부에도 도움이 됐으면 좋겠다.
출처
바이너리 코드, 기계어, 바이트 코드, 플랫폼
https://shrtorznzl.tistory.com/82
JVM과 JIT 컴파일러 설명
https://catch-me-java.tistory.com/11?category=438116
JIT 컴파일러 설명
blog.naver.com/PostView.nhn?blogId=kbh3983&logNo=220985785358
https://nopainnocode.tistory.com/15
www.baeldung.com/graal-java-jit-compiler
JAVA 컴파일에서 실행까지
- 컴파일 과정, 중간코드 생성 부분
https://homoefficio.github.io/2019/01/31/Back-to-the-Essence-Java-컴파일에서-실행까지-1/
- JDK, JRE, JVM 부분
https://homoefficio.github.io/2019/01/31/Back-to-the-Essence-Java-컴파일에서-실행까지-2/
'JAVA > JVM' 카테고리의 다른 글
가비지 컬렉터(Garbage Collector) - 가비지 컬렉션 종류와 알고리즘 (0) | 2021.04.10 |
---|---|
가비지 컬렉터(Garbage Collector) - 가비지 개념과 가비지 컬렉션 프로세스 (0) | 2021.04.08 |
메모리 영역(Runtime Data Area, JVM Memory) (0) | 2021.03.28 |
클래스로더(Class Loader) (0) | 2021.03.28 |
JIT(Just In Time) 컴파일러 (0) | 2021.01.03 |