1. 개요
Java Virtual Machine(자바 가상 머신)은 Java로 개발한 프로그램을 컴파일하여 만들어지는 바이트코드를 실행시키기 위한 가상머신이다. JRE(Java Runtime Environment)에 포함되어 있으며, Java 컴파일러가 프론트엔드를 담당한다면 Java 가상 머신은 코드 최적화와 백엔드를 담당한다.(웹이 아닌 컴파일러의 프론트엔드/백엔드를 말하는 것이다.)2. 상세
Java와 함께 썬 마이크로시스템즈에서 개발되었으며 썬이 오라클에 인수된 후 현재는 오라클이 Java 명칭을 비롯하여 모든 권한을 행사하고 있다. Java 소스 코드는 javac 컴파일러를 거쳐 바이트코드로 변환되며, 이 바이트코드는 JRE에 들어있는 java classloader에 의해 JVM으로 적재되고 JVM은 적재된 바이트코드를 JIT 컴파일 방식으로 실행하는 컴퓨터의 OS 및 CPU 아키텍처용 기계어로 번역되어 수행된다.JVM은 플랫폼 독립적으로, JVM이 실행 가능한 환경이라면 어디서든 Java 프로그램이 실행될 수 있도록 한다. 즉, Java의 모토인 Write once, Run anywhere는 JVM을 통해 가능한 것이다. 하지만 특정 운영체제의 특수한 기능을 호출하거나 하드웨어를 제어하는 등의 일은 JVM으로 할 수 없으며, JNI 같은 Native 코드를 호출하기 위한 인터페이스를 거쳐야 한다. 즉 일종의 샌드박스 환경인 셈이다.
저수준 프로그래밍 언어로 개발하는 사람이라면 알겠지만 CPU 아키텍처마다 기계어가 다르기 때문에 한 OS만 지원하더라도 각 CPU용의 프로그램으로 컴파일해줘야 한다. 주로 x86-x64(x86 및 AMD64)와 AArch64(ARM64) 버전으로 제작한다.
Java 가상머신이라고 해서 Java 바이트코드만 인식하는 것은 아니다. 이 바이트코드를 Java가 아닌 다른 언어( Kotlin이나 Scala, Groovy 같은 언어)를 가지고도 생성할 수 있기 때문이다. 따라서 지금의 Java 가상머신은 Java만을 위한 것이라고 생각하면 안 된다. 이름이 Java 가상머신이다 보니 Java에 종속된 게 아닌가 생각할 수도 있지만 그렇지도 않다. Java 가상머신은 코틀린 코드를 컴파일해도 읽을 수도 있고 스칼라 코드를 컴파일해도 읽을 수 있다. 단, 기본이 Java를 위해서 만들어졌기 때문에 Java 소스 코드와 컴파일된 바이트코드는 비교적 직관적으로 연결되지만 코틀린이나 스칼라의 경우 Java와의 호환성을 고려하긴 했어도 상대적으로는 비직관적이며 필연적으로 Java를 공부해야 한다는 단점이 있다.
3. 종류
오라클이 소유한 두 종류의 JVM으로 썬 마이크로시스템즈에서 개발된 HotSpot과 BEA 시스템에서 개발된 JRockit이 있고, 클린 룸 구현으로는 Kafee와 IBM사의 IBM J9, IBM J9을 기반으로 한 Eclipse OpenJ9이 있다. 윈도우, 리눅스 등의 환경에서는 대부분 HotSpot이 사용되지만, IBM AIX를 운영체제로 사용하는 경우 IBM J9가 널리 사용된다.GraalVM이라는 JVM이 오라클에 의해 오픈소스로 개발되고 있다.
구글의 안드로이드는 Java를 사용하여 개발되었지만, 안드로이드에 있는 달빅 가상 머신은 Java 바이트코드를 전혀 실행하지 못하므로 엄밀하게 말하면 JVM은 아니다. 다만 Java 바이트코드를 한 번 변환해서 달빅용 코드[1]로 만들고 그걸 실행한다. 좀 바보같긴 하지만, JVM을 포함한 Java의 여러 부분에 걸린 특허를 피하기 위해 내놓은 꼼수. 다만 결국 썬을 인수한 오라클이 이 문제로 소송을 걸었다. 결국 속도 문제[2]도 해결할 겸 구글은 달빅을 포기하고 안드로이드 런타임으로 변경했다. 다만 안드로이드 런타임도 여전히 달빅과 동일한 바이트코드를 사용한다.
Microsoft에서도 JVM을 내놓은 적이 있었다. Java Applet 실행을 위해 Internet Explorer 3부터 제공했다. 그러나 1997년 썬에게 고소당했는데, 이는 MS가 Java 1.1 표준을 무시한 채 자신들 입맛대로 뜯어고친 Visual J++를 만들었기 때문이다. MS의 이러한 행보에는 의도적인 비호환성을 통해 결국 Java의 발목을 잡게 하려는 꼼수가 있었다는 지적이 많았다. 결국 2001년 MS는 Microsoft JVM을 단계적으로 폐기하기로 합의하였으며, 2003년을 마지막으로 Microsoft JVM의 개발이 완전히 중단되었다. 단, 보안 업데이트는 2007년까지 지원되었다.
자바 프로세서라는 자바 바이트코드를 명령어 세트로 사용하는 CPU가 개발되기도 했었다.
4. 구조
5. 성능
바이트코드는 실제의 기계에서 직접 실행되는 것이 아니라 JVM의 해석 단계를 거쳐 실행되므로 Java로 개발된 프로그램은 같은 기능의 네이티브 언어[3]보다 실행 속도가 느리다. 과거에는 바이트코드를 순수하게 인터프리트하여 매우 느렸으나 현재는 JIT 컴파일의 도입과 하드웨어의 발전으로 성능이 개선되었다.메모리의 접근을 가상 머신 차원에서 관리하고 있으므로 런타임에 최적화가 가능하다. 그러나 인위적으로 만들어진 매우 극단적이고 특수한 상황에서 그것도 극히 일부 기능에 대해서만 네이티브 언어보다 우월한 성능을 보여 준다. JIT 컴파일 시간, 가비지 컬렉션을 위한 시간 등이 필요하므로 근본적인 한계가 있다.[4]
5.1. 가비지 컬렉션 (GC)
JVM은 가비지 컬렉션을 수행하여 할당되었다가 더 이상 쓰이지 않는 메모리를 자동으로 회수한다. Full GC(전체 가비지 컬렉션)의 경우 프로그램 수행을 일시 정지(Stop-the-World) 시켜놓고 할 수밖에 없는데, 또 이게 Java 프로그램이 규칙적이지도 않고 이유도 없이 뚝뚝 끊긴다는 악명을 떨치는데 공헌했다. 대규모 서비스의 운영 시 Full GC는 성능에 상당히 심각한 영향을 주므로 프로그래머의 GC 튜닝이 필수로 들어간다. 최신 버전(11~12 이후)의 JVM에는 힙의 크기와 상관없이(수백 MB ~ 수십 TB까지) 일시정지 시간이 10ms 이하인 GC 알고리즘들(ZGC, Shenandoah)이 탑재되어 있다.JVM 모니터링 클라이언트 VisualVM[5]과 그 플러그인 Visual GC를 설치하면 가비지 컬렉팅 상황을 시각적으로 확인할 수 있다. # VisualVM 디렉토리의 etc 폴더에 가면 visualvm.conf라는 파일이 있는데, 이 파일에 적혀 있는 visualvm_jdkhome 항목의 주석(#)을 제거한 후 JDK 폴더의 경로를 적으면 자신이 원하는 버전의 JDK로 모니터링이 가능하다.
GC의 상세한 동작 과정에 대해서는 이 문서를 참고할 것.
6. JVM 기반 언어
이름에 Java가 들어 있지만 JVM은 자신이 무슨 언어를 실행하는지 전혀 관심이 없다. 자바 바이트코드로 변환이 가능하다면 Java가 아니라 Java 바이트코드를 만드는 다른 언어도 얼마든지 실행할 수 있는 것이다.JVM의 플랫폼 독립성과 준수한 성능, 그리고 Java 코드와의 손쉬운 상호작용 등의 특징으로 인하여 JVM을 기반으로 하는 Java 이외의 언어들도 많이 개발되어 있다.