개발 언어/Java

[java] JVM 이란?

피어나는 열정 2021. 10. 27. 18:56

- 특징

- 단점

- 차이

- 지원 언어

 

1)JVM 특징

 

  • 스택 기반의 가상 머신
    : 대표적인 컴퓨터 아키텍처인 인텔 x86아키텍처, ARM 아키텍처와 같은 하드웨어가 레지스터 기반으로 동작하는 데 비해 JVM은 스택 기반으로 동작한다.
  • 심볼릭 레퍼런스
    : 기본 자료형(primity data type)을 제외한 모든 타입(클래스와 인터페이스)을 명시적인 메모리 주소 기반의 레퍼런스가 아니라 심볼릭 레퍼런스를 통해 참조한다.
  • 기본 자료형을 명확하게 정의하여 플랫폼 독립성 보장
    : C/C++등의 전통적인 언어는 플랫폼에 따라 int형의 크기가 변한다. JVM은 기본 자료형을 명확하게 정의하여 호환성을 유지하고 플랫폼 독립성을 보장
  • 네트워크 바이트 오더(network byte order)
    : 자바 클래스 파일은 네트워크 바이트 오더를 사용한다. 인텔 x86아키텍처가 사용하는 리틀 엔디안이나, RISC 계열 아키텍처가 주로 사용하는 빅 엔디안 사이에서 플랫폼 독립성을 유지하려면 고정된 바이트 오더를 유지해야 하므로 네트워크 전송 시에 사용하는 바이트 오더인 네트워크 바이트 오더를 사용한다. 네트워크 바이트 오더는 빅 엔디안이다.

 

 

 

 

Java Compiler(자바 컴파일러)에 의해 Java source(.java) 파일은  Byte code로 변환된다.

 

Class Loader(클래스 로더)는 이 변환된 Byte code(.class) 파일을 JVM 내로 class를 로드하고 Link작업을 통해 배치 등 일련의 작업을 한다. 또 런타임시 class를 load한다.

 

Execution Engine(실행 엔진) 은 Class Loader를 통해 JVM 내부로 넘어와 Runtime Data Area(JVM 메모리)에 배치된 Byte code들을 명령어 단위로 실행시킨다.

 

GC(Garbage Collector)는 어플리케이션이 생성한 객체의 생존 여부를 판단하여, 더이상 참조되지 않거나 null 인 객체의 메모리를 해체시켜 메모리 반납을 한다. 

 

Runtime Data Areas(JVM 메모리) 런타임 데이터 영역은 JVM메모리로 Java 어플리케이션이 실행하면서 할당받은 메모리영역이다.

 

 

 

 

2)JVM 단점

1-1. JVM 로딩 속도 문제

자바의 심각한 단점 중 하나는, 실행하는 과정에서 JVM(Java Virtual Machine)이 반드시 완벽하게 로딩되어야 하기 때문에 프로그램의 초기 시작 시간이 완전한 이진 코드로 컴파일된 프로그램을 실행하는 것에 비해 오래 걸리는 것이다. 단적인 예로, 아무것도 안 하고 콘솔 화면에 달랑 "Hello, World!"라고 찍기만 하는 프로그램이 실행되는 데에도 thread가 10개쯤 뜨는데, 특히 그 프로그램에 AWT, Swing, SQL 같이 불필요한 기능을 끌어들이는 것은 매우 심각한 문제다. 이 문제는 런타임 자체가 아직 모듈화 되지 않았다는 점에서 기인한다.

 

하지만 요즘 같은 고사양 컴퓨터에서는 아주 많은 라이브러리를 끌어오는 것이 아니라면 체감상 차이는 크게 나지 않는다. 또한 Java 9부터는 드디어 런타임 라이브러리를 모듈화하고 있으므로, 필요한 모듈만 끌어서 프로그램을 짤 수 있습니다.

 

1-2. 가상 머신 바이트코드 실행 속도 문제

C/C++, Pascal, Fortran과 같은 언어와 달리, 자바는 바이트코드로 된 프로그램을 실행하기 위해 운영체제와 프로그램 사이에 JVM이라는 두꺼운 계층이 하나 더 자리 잡게 된다. 그리고 바이트코드는 실시간으로 각 타깃 플랫폼용 기계어로 번역되어 실행된다. 이로 인해 네이티브 바이너리 코드를 출력하는 언어와 비교하여 실행 속도와 성능에 일정 부분 손실이 발생할 수밖에 없다. AWT, Swing 같은 GUI 라이브러리를 사용할 때도 심각하게 느린 것을 체감할 수 있다. 이런 문제점을 썬 마이크로시스템즈도 곧 깨달았고, 최초 발표에서 2년 후인 1998년부터 JIT 컴파일러를 JVM에 내장하여 성능이 상당 부분 개선되었다. 하지만, 그만큼 메모리가 뒷받침해줘야 한다. 현재는 보통 같은 기능/알고리즘을 실행하는 데 C++ 보다 2~3배 정도의 시간이 더 필요하다고 알려져 있다. 이 부분은 꽤 초기부터 지속적으로 개선되어 왔기 때문에 현재 실행 속도 자체에 대한 이슈는 예전에 비해 많이 줄어든 편이긴 한다.

 

이 문제는 Java 9에서 '선행 컴파일'이라는 이름으로 개선될 예정이다. JIT 컴파일로 실행과 동시에 컴파일을 하는 게 아니라 기존의 정적 컴파일처럼 바이트코드를 미리 기계어로 번역하면, 컴파일 속도는 다소 느려지지만 실행 속도는 빨라지게 된다. 물론 컴파일 한 번으로 여러 플랫폼에서 동일하게 실행시키는 건 불가능해진다.

 

1-3. 가비지 컬렉션에 의한 실행 지연 문제

가비지 컬렉션에 의한 메모리 프리징 현상이 초반부터 지속적으로 자바를 괴롭혔다. 멀쩡하게 동작해야 할 프로그램이 순간적으로 뚝 뚝 끊기는 듯한 현상이 발생하는 것이다. 오늘날 자바의 문제는 바이트코드 변환으로 인한 속도 저하보다 이 가비지 컬렉션의 영향이 더 크다고 할 수 있다. 이러한 문제점은 가비지 컬렉션을 지원하는 다른 프로그래밍 언어들도 마찬가지이긴 하지만 실행 속도와 함께 자바 초기부터 꾸준히 문제로 꼽혀온 것으로, 버전이 올라갈 때마다 다양하게 개선되어 왔다.

 

Java 8부터는 메모리 누수를 일으키던 메서드 영역의 PermGen Area를 제거하여 static 인스턴스와 리터럴 문자열도 GC의 대상이 되도록 바뀌었으며, 클래스, 메서드, 배열의 메타 정보는 동적 리사이징이 가능한 Metaspace로 이동시켜 시스템 힙 영역에 저장된다. 덕분에 JVM 힙 영역의 공간이 늘어나고 PermGen Area를 스캔/삭제할 필요가 없어져 GC의 성능이 대폭 향상되었다.

 

 

3)JVM,JRE,JDK 차이

JAVA는 어떤 운영체제에서도 동일한 형태로 실행시킬 수 있는 특징이있다. 이를 가능하게 해주는 것은 JVM이다. JVM(Java Virtual Machine)은 자바 가상 머신의 줄임말로 자바프로그램 등을 컴파일하여 만들어진 바이트 코드를 실행해주는 가상머신이다.

JAVA 언어를 작성된 프로그램을 실행하기 위해선 JRE(Java SE Runtime Environment)가 필요하다. JAVA언어를 사용하는 개발자가 아니라 JAVA언어로 만들어진 프로그램을 실행하는 사용자라면 JRE만 컴퓨터에 설치하면 된다.

JAVA 언어 개발자는 JAVA언어로 작성된 소스를 컴파일하고 관리한다. 이때 사용되는 도구를 JDK(Java SE Development Kit)라고 한다. JDK안에는 JRE도 포함된다. 컴파일한 결과를 실행하기 위해서는 JRE가 필요하기 때문이다.

따라서 JDK를 설치할 때 환경설정을 해줘야 정상적으로 작동이 된다. 운영체제 마다 환경 설정 방법은 다르지만, 설치해야할 환경 변수의 이름은 동일하다.

정리해보면,

  • JDK: Java SE Development Kit, 자바 개발 키트, JRE+컴파일러, 디버거 등의 개발도구
  • JRE: Java SE Runtime Environment, 자바 구동 환경, JVM+시스템 라이브러리
  • JVM: Java Virtual Machine, 자바 가상 머신, 운영체제 독립적으로 동작하기 위해 필요
  • IDE: Integrated Development Environment, 통합 개발 환경
  • API: Application Programming Interface, JAVA에서 기본으로 제공하는 클래스들의 모음

4)JVM 지원 언어

kotlin

IntelliJ IDEA의 개발사 JetBrains에서 2011년에 공개한 오픈 소스 프로그래밍 언어. JVM 기반의 언어이며, Java와 유사하지만 더 간결한 문법과 다양한 기능을 추가하였다. Java와의 상호 운용이 100% 지원된다. JVM 바이트코드가 기본이지만, Kotlin/Native 컴파일러를 사용하여 기계어로 컴파일할 수 있다. 안드로이드, 스프링 프레임워크, 톰캣, JavaScript, Java EE, HTML5, iOS, 라즈베리 파이 등을 개발할 때 사용할 수 있다.

 

JVM 구성요소

JVM 구성 요소

JVM은 크게 아래와 같이 이루어져 있다.

  • 클래스 로더(Class Loader)
  • 실행 엔진(Execution Engine)
    • 인터프리터(Interpreter)
    • JIT 컴파일러(Just-in-Time)
    • 가비지 콜렉터(Garbage collector)
  • 런타임 데이터 영역 (Runtime Data Area)

클래스 로더
JVM 내로 클래스 파일(*.class)을 로드하고, 링크를 통해 배치하는 작업을 수행하는 모듈이다.
런 타임시 동적으로 클래스를 로드하고 jar 파일 내 저장된 클래스들을 JVM 위에 탑재한다.
즉, 클래스를 처음으로 참조할 때, 해당 클래스를 로드하고 링크한는 역할을 한다.

 

실행 엔진

클래스를 실행시키는 역할이다.

클래스 로더가 JVM내의 런타임 데이터 영역에 바이트 코드를 배치시키고, 이것은 실행 엔진에 의해 실행된다.

자바 바이트 코드(*.class)는 기계가 바로 수행할 수 있는 언어보다는 비교적 인간이 보기 편한 형태로 기술된 것이다. 그래서 실행 엔진은 이와 같은 바이트 코드를 실제로 JVM 내부에서 기계가 실행할 수 있는 형태로 변경한다.

 

 ↳ 인터프리터

실행 엔진은 자바 바이트 코드를 명령어 단위로 읽어서 실행한다.

하지만 한 줄씩 수행하기 때문에 느리다는 단점이 있다.

 ↳ JIT(Just-In-Time)

인터프리터 방식으로 실행하다가 적절한 시점에 바이트 코드 전체를 컴파일하여 기계어로 변경하고, 이후에는 해당 더 이상 인터프리팅 하지 않고 기계어로 직접 실행하는 방식이다.

 

가비지 콜렉터

더이상 사용되지 않는 인스턴스를 찾아 메모리에서 삭제함.

 

Runtime Data Area

Runtime Area

프로그램을 수행하기 위해 OS에서 할당받은 메모리 공간

 

 ↳ PC Register

Thread가 시작될 때 생성되며 생성될 때마다 생성되는 공간으로, 스레드마다 하나씩 존재한다.

Thread가 어떤 부분을 어떤 명령으로 실행해야할 지에 대한 기록을 하는 부분으로 현재 수행 중인 JVM 명령의 주소를 갖는다.

 

💡 프로세스(process)란?
단순히 실행 중인 프로그램(program)

즉, 사용자가 작성한 프로그램이 운영체제에 의해 메모리 공간을 할당받아 실행 중인 것을 말한다.

이러한 프로세스는 프로그램에 사용되는 데이터와 메모리 등의 자원 그리고 스레드로 구성된다.
💡 스레드(thread)란?

스레드(thread)란 프로세스(process) 내에서 실제로 작업을 수행하는 주체를 의미

모든 프로세스에는 한 개 이상의 스레드가 존재하여 작업을 수행한다.

또한, 두 개 이상의 스레드를 가지는 프로세스를 멀티스레드 프로세스(multi-threaded process)라고 한다.

 

 ↳ JVM 스택 영역

프로그램 실행과정에서 임시로 할당되었다가 메소드를 빠져나가면 바로 소멸되는 특성의 데이터를 저장하기 위한 영역이다.

각종 형태의 변수나 임시 데이터, 스레드나 메소드의 정보를 저장한다.

메소드 호출 시마다 각각의 스택 프레임(그 메서드만을 위한 공간)이 생성된다. 메서드 수행이 끝나면 프레임 별로 삭제를 한다.

메소드 안에서 사용되는 값들을 저장한다. 또 호출된 메소드의 매개변수, 지역변수, 리턴 값 및 연산 시 일어나는 값들을 임시로 저장한다.

 

 ↳ Native method stack

자바 프로그램이 컴파일되어 생성되는 바이트 코드가 아닌 실제 실행할 수 있는 기계어로 작성된 프로그램을 실행시키는 영역.

JAVA가 아닌 다른 언어로 작성된 코드를 위한 공간.

Java Native Interface를 통해 바이트 코드로 전환하여 저장하게 된다.

일반 프로그램처럼 커널이 스택을 잡아 독자적으로 프로그램을 실행시키는 영역

 

 ↳ Method Area (= Class Area = Static area)

클래스 정보를 처음 메모리 공간에 올릴 때 초기화되는 대상을 저장하기 위한 메모리 공간

 

 ↳  ↳ Runtime Constant Pool

스태틱 영역에 존재하는 별도의 관리영역.

상수 자료형을 저장하여 참조하고 중복을 막는 역할을 수행한다.

 

💡 스태틱 영역에 저장되는 데이터

Field Information (멤버 변수!)
멤버변수의 이름, 데이터 타입, 접근 제어자에 대한 정보

Method Information (메소드!)
메소드의 이름, 리턴타입, 매개변수, 접근 제어자에 대한 정보

Type Information (타입!)
class인지 interface인지의 여부 저장. Type의 속성, 전체 이름, super 클래스의 전체 이름. (interface이거나 object인 경우 제외된다. 이건 Heap에서 관리함)

 

 

 

https://happy-coding-day.tistory.com/123

https://goodgid.github.io/Java-8-JVM-Metaspace/

https://johngrib.github.io/wiki/java8-why-permgen-removed/