개발자

"JVM이란 무엇인가" 자바 가상 머신 이해하기

Matthew Tyson | JavaWorld 2018.09.19
자바 가상 머신(Java Virtual Machine, JVM)은 시스템 메모리를 관리하면서 자바 기반 애플리케이션을 위해 이식 가능한 실행 환경을 제공한다.


Credit: JavaWorld/ IDG

JVM은 다른 프로그램을 실행시키는 것이 목적인 프로그램이다. 방어적 코딩(Coding Kung Fu)의 가장 훌륭한 예 가운데 하나이기도 한 간단한 아이디어이기도 하다. JVM은 당시에도 상황을 뒤집어 놓았고 지금까지도 프로그래밍 혁신을 지원하고 있다.

JVM의 용도와 정의
JVM에는 2가지 기본 기능이 있다. 자바 프로그램이 어느 기기, 또는 어느 운영체제 상에서도 실행될 수 있게 하는 것("한 번 작성해, 어디에서나 실행한다"는 유명한 원칙이다)과 프로그램 메모리를 관리하고 최적화하는 것이다. 1995년 자바가 공개 되었을 때, 모든 컴퓨터 프로그램은 특정 운영체제에 맞게 작성되었으며, 프로그램 메모리는 소프트웨어 개발자가 관리했었다. 그래서 JVM은 놀라움이었다.

그림. JVM에 대한 상위 레벨 뷰

Credit: JavaWorld/ IDG

JVM에 대한 기술적인 정의를 하는 것은 유용하며, 소프트웨어 개발자들의 일반적인 정의도 있다. JVM에 대한 정의는 다음과 같이 나눌 수 있다.

- 기술적 정의: JVM은 코드를 실행하고 해당 코드에 대해 런타임 환경을 제공하는 소프트웨어 프로그램에 대한 사양(Specification)이다.
- 일반적 정의: JVM은 자바 프로그램을 실행하는 방법이다. JVM의 설정을 구성한 다음 설정사항에 따라 실행 중에 프로그램 리소스를 관리한다.

개발자들이 JVM에 대해 말하고 있다면, 대개는 어떤 기기 상에서 실행되고 있는 프로세스, 특히 자바 앱에 대한 리소스를 대표하고 통제하는 서버를 지칭한다. 이 맥락에서의 JVM은 이런 작업들을 수행하는 프로그램 구축을 위한 요구조건들을 정의하는 JVM 사양과는 다른 것이다.

누가 JVM을 개발하며 유지보수하는가
JVM은 널리 배포되어 있고, 많이 사용되고 있으며, 몇몇 매우 뛰어난 프로그래머들에 의해 유지 보수되고 있다, 기업과 오픈소스 양면에서. 오픈JDK(OpenJDK)는 자바에 대한 오라클의 책무 기간 중에도 계속되고 있으며, 요즘의 어려운 작업 대부분은 오라클 엔지니어들이 수행하고 있다.

JVM에서의 메모리 관리
JVM 실행에 있어서 가장 일반적인 상호작용은 힙(Heap)과 스택(Stack)의 메모리 사용을 확인하는 것이다. 가장 흔한 교정작업은 JVM의 메모리 설정 값들을 조율하는 것이다.

- 가비지 컬렉션(Garbage Collection)
자바 이전에는 프로그래머가 모든 프로그램 메모리를 관리했었다. 자바에서는 JVM이 프로그램 메모리를 관리한다. JVM은 가비지 컬렉션이란 프로세스를 통해 메모리를 관리하며, 이 가비지 컬렉션은 자바 프로그램에서 사용되지 않는 메모리를 지속적으로 찾아내서 제거한다. 가비지 컬렉션은 실행 중인 JVM 내부에서 일어난다.
초창기에, 자바는 C++만큼 "메탈에 가깝지 않아 C++처럼 빠르지 않다"는 면에서 많은 비평을 받았다. 특히 가비지 컬렉션 프로세스가 논쟁이 되었다. 그 이후, 가비지 컬렉션을 위해 다양한 알고리즘과 접근방식이 제안되고 사용되었다. 일관성 있는 개발과 최적화를 통해 가비지 컬렉션은 크게 개선되었다.

- "메탈에 가까운"이란 말의 의미
프로그래머가 어떤 프로그래밍 언어나 플랫폼이 "메탈에 가깝다"고 말하는 경우, 이는 개발자가 운영체제의 메모리를 프로그램적으로 (코드를 작성함으로써) 관리할 수 있다는 것을 의미한다. 이론적으로, 프로그래머는 얼마나 많은 메모리를 사용하고 언제 폐기할 지를 명시함으로써 프로그램에서 더 높은 성능을 끌어낼 수 있다. 대부분의 경우, 개발자가 하는 것보다 JVM 같은 정제된 프로세스에 메모리 관리를 위임함으로써 더 높은 성능과 더 적은 오류를 도출할 수 있다.

세 부분으로 구성된 JVM
JVM에는 3가지 측면이 있다고 할 수 있다. 표준(Specification), 구현(Implementation ) 그리고 인스턴스(Instance)인데, 각각에 대해 살펴보자.

1. JVM 사양
첫째, JVM은 소프트웨어 사양이다. 다소 순환적인 방식으로, JVM 사양은 구현에 있어 최대한의 창조성을 허용하기 위해, JVM 구현 세부사항이 사양 안에 정의되어 있지 않다고 강조하고 있다.

"자바 가상 머신을 올바르게 구현하려면, 클래스(Class) 파일 포맷을 읽고 그 안에 지정된 작업을 제대로 수행하기만 하면 된다."

요한 세바스찬 바흐도 음악을 비슷하게 창조한다고 기술했다.

"정확한 시점에 올바른 건반을 누르기만 하면 된다."

결국, JVM이 해야만 하는 일은 자바 프로그램을 정확하게 실행하는 것뿐이다. 간단해 보인다, 심지어 겉으로 보기에는 단순해 보이기도 하지만, 자바 언어의 능력과 유연성을 고려할 때, 이것은 엄청나게 힘든 일이다.

2. JVM 구현
JVM 사양 구현은 실제 소프트웨어 프로그램을 도출하며, 이것이 JVM 구현이다. 실제로, 오픈소스와 특정 업체 고유의 JVM 구현이 다수 존재한다. 오픈JDK의 핫스팟(HotSpot) JVM은 참조 구현이며, 세계에서 가장 철저하게 증명된 코드기반 중 하나로 남아있다. 핫스팟은 가장 널리 사용되는 JVM이기도 하다.

오라클의 라이선스가 부여된 JDK를 포함해, 라이선스가 부여되는 거의 모든 JVM은 오픈JDK와 핫스팟 JVM의 포크(Fork)로 생성된 것이다. 오픈JDK로부터 허가받은 포크를 생성하는 개발자들은 종종 운영체제 고유의 성능 개선사항들을 추가하려는 욕구에 의해 동기 부여된다. 일반적으로, 개발자는 JRE(Java Runtime Environment) 번들의 한 부분으로 JVM을 다운로드해 설치한다.

3. JVM 인스턴스(Instance)
JVM 스펙이 구현돼서 소프트웨어 제품으로 릴리즈되면, 개발자는 그것을 하나의 프로그램처럼 다운로드해 실행할 수 있다. 이렇게 다운로드 된 프로그램이 하나의 JVM 인스턴스(또는 인스턴스화된 버전)이다.

개발자들이 "JVM"에 대해 말하는 경우, 대부분의 경우에는 소프트웨어 개발 환경 또는 제품화 환경에서 실행되는 하나의 JVM 인스턴스를 지칭한다. "아난드, 그 서버에 있는 JVM은 메모리를 얼마나 사용하고 있어?" 또는 "순환 호출(Circular Call)을 하는 바람에 스택 오버플로우 에러가 내 JVM을 망가뜨렸다니, 믿을 수가 없군. 이런 초보적인 실수를 하다니!"라고 말할 지도 모른다.

가상 머신(Virtual Machine)으로서의 JVM
JVM은 자바 클래스 파일을 이식성 있는 방식으로 실행하는 가상 머신이다. 가상 머신이란 JVM이 하부에 있는, 실제 머신–예를 들면, 프로그램이 실행되는 서버–의 추상화(Abstraction)를 의미한다. 사용 중인 운영체제 또는 실제로 존재하는 하드웨어에 관계없이, JVM은 그 안에서 실행될 프로그램을 위한 예측 가능한 환경을 조성한다. 하지만, 진정한 가상 머신과 달리 JVM은 가상 운영체제를 생성하지 않는다. JVM을 관리되는 런타임 환경(Managed Runtime Environment) 또는 프로세스 가상 머신이라고 정의하는 것이 더 정확할 수도 있다.

소프트웨어 사양이란
소프트웨어 사양(또는 스펙)이란 어떤 소프트웨어 시스템이 어떻게 동작해야 하는 지를 정의하는 인간이 읽을 수 있는 설계 문서다. 사양의 목적은 엔지니어들이 코딩할 수 있도록 정확한 정의와 요구사항을 기술하는 것이다.

JVM에서 클래스 파일 로드와 실행
자바 애플리케이션 실행에 있어 JVM의 역할에 대해 살펴봤다. 그런데 이 기능을 실제로는 어떻게 수행할까. 자바 애플리케이션을 실행하기 위해 JVM은 자바 클래스 로더(Class Loader)와 자바 실행 엔진(Execution Engine)에 의존한다.

- JVM 자바 클래스 로더
자바에서는 모든 것이 클래스이며, 모든 자바 애플리케이션은 클래스로부터 만들어진다. 하나의 애플리케이션이 하나 또는 수천 개의 클래스로 구성될 수 있다. 자바 애플리케이션을 실행하기 위해서는, JVM이 컴파일된 .class 파일을 맥락에 따라 액세스 가능한 서버 같은 것으로 로드 해야만 한다. JVM은 클래스 로더에 의존해 이 기능을 수행한다.

자바 클래스 로더는 클래스를 메모리에 로드하고 실행을 위해 사용할 수 있게 만드는 JVM의 일부다. 클래스 로더는 클래스 로딩(Class Loading)을 최대한 효율적으로 수행하기 위해 지연 로딩(Lazy-loading)과 캐싱(Caching) 같은 기법을 활용한다. 그렇긴 하지만, 클래스 로딩이 이식성 있는 런타임 메모리 관리처럼 대단히 까다로운 것이 아니기 때문에 이런 기법들은 비교적 간단하다.

모든 자바 가상 머신에는 클래스 로더가 포함되어 있다. JVM 스펙은 런타임에 클래스 로더에 질의(Query: 쿼리)하고 조작하기 위한 일반적인 방법들을 설명하고 있지만, 이런 기능을 충족시키는 책임은 JVM 구현에 있다. 개발자의 관점에서 볼 때, 근본적인 클래스 로더 메커니즘은 대개 블랙 박스다.

- JVM 실행 엔진
클래스 로더가 클래스를 로딩하는 작업을 마치면, JVM은 각 클래스에 있는 코드를 실행하기 시작한다. 이 기능을 처리하는 것이 JVM 구성요소인 실행 엔진이다. 실행 엔진은 JVM 실행에 필수적이다. 실제로, 현실적인 목적을 위해, 실행 엔진은 JVM 인스턴스다.

코드 실행에는 시스템 리소스에 대한 액세스 관리가 관련되어 있다. JVM 실행 엔진은–파일, 네트워크 그리고 메모리 리소스를 요구하는–프로그램 실행과 운영체제 가운데 위치해서 이런 리소스들을 공급한다.

- 실행 엔진이 시스템 리소스를 관리하는 방법
시스템 리소스는 크게 메모리와 다른 모든 것, 2가지 범주로 나눌 수 있다.

JVM이 미사용 메모리를 없애야 할 책임이 있으며, 가비지 컬렉션이 이런 폐기를 수행하는 메커니즘이라는 것을 상기하라. JVM은 개발자들이 당연하게 여기고 있는 참조 구조체(Referential Structure)를 할당하고 관리할 책임도 있다. 예를 들어, JVM의 실행 엔진은 자바의 new 키워드 같은 것을 받아, 그것을 메모리 할당에 대한 운영체제 고유의 요청으로 변환할 책임이 있다.

메모리를 넘어서, 실행 엔진은 파일 시스템 액세스, 네트워크 입출력을 위한 리소스를 관리한다. JVM이 여러 운영체제 전반에서 호환되기 때문에, 실행 엔진은 각각의 운영 체제 환경에 즉각 대응할 수 있어야만 한다. 이것이 JVM이 종잡을 수 없는 요구사항을 처리하는 방법이다.

JVM의 과거, 현재, 미래
1995년, JVM은 이후 최신 소프트웨어 개발에 대한 기준이 된 2가지 혁신적인 개념을 소개했다. "한 번 작성해, 모든 곳에서 실행", 그리고 자동 메모리 관리였다. 당시에는 소프트웨어 상호운영성이 대담한 개념이었지만, 지금은 소수의 개발자들만이 재고할 뿐이다. 마찬가지로, 우리 엔지니어 선배들이 프로그램 메모리를 스스로 관리했던 것처럼, 우리 세대는 가비지 컬렉션과 함께 성장했다.

현대 프로그래밍을 발명한 사람은 제임스 고슬링과 브렌단 아이크지만, 수천 명의 다른 사람들이 다음 수십 년 동안 그들의 생각을 다듬고 쌓아왔다. 처음에는 자바만을 위한 자바 가상 머신이었지만, 이제는 진화해 스칼라(Scalar), 그루비(Groovy), 그리고 코틀린(Kotlin)을 포함한 수 많은 스크립팅 언어와 프로그래밍 언어를 지원하고 있다. 앞으로, JVM이 개발 환경의 중요한 부분이 아닌 미래를 보는 것은 어렵다. editor@itworld.co.kr  

회사명 : 한국IDG | 제호: ITWorld | 주소 : 서울시 중구 세종대로 23, 4층 우)04512
| 등록번호 : 서울 아00743 등록발행일자 : 2009년 01월 19일

발행인 : 박형미 | 편집인 : 박재곤 | 청소년보호책임자 : 한정규
| 사업자 등록번호 : 214-87-22467 Tel : 02-558-6950

Copyright © 2024 International Data Group. All rights reserved.