[Java] 자바 가상 머신(Java Virtual Machine, JVM), 자바의 특성을 곁들여서

최지수·2022년 2월 20일
0

Java

목록 보기
2/27
post-thumbnail

잘 정리된 블로그가 있어 정리해봅니다 ㅎㅎ

자바 가상 머신(Java Virtual Machine, JVM)

자바 가상 머신JVM은 자바 코드와 어플리케이션을 구동하는, 런타임 환경을 제공하는 엔진이에요. 자바 소스를 컴파일한 결과물인 바이트byte 코드를 기계어로 변환시켜줘요.

JVM은 JRE의 일부에요. 다른 프로그래밍 언어에선 컴파일러가 일부 시스템에 적합한 기계어를 제공하는 반면, 자바 컴파일러는 JVM이라는 가상 머신이 기계어로 변환하기 위한 코드(아까 말한 바이트 코드에요)를 만들어줘요.

자바 컴파일러의 결과인 바이트 코드는 시스템 중립적이고 JVM이 시스템에 따라 적절한 기계어로 변환해주기 떄문에 모든 플랫폼에 적용할 수 있는 크로스-플랫폼Cross-platform 환경을 제공해요.

JVM 아키텍쳐

JVM의 구조는 클래스로더Classloader, 메모리 영역Memory Area 그리고 실행 엔진Execution Engine 등으로 나눌 수 있어요.

Class Loader

Classloader는 자바 컴파일러를 통해 생성된 바이트 코드, .class 확장명의 파일을 JVM에 적재Loading해요. 초기화Intialization, 로딩Loading, 바이트 코드를 서로 연결하는 링크Linking이라는 기능들을 수행해요.

적재가 완료되면 자바로 만들어진 바이트 코드들을 JVM Memory에 저장되요.

JVM Memory

Method Area

JVM은 프로그램이 실행 중에 클래스가 사용될 때 해당 클래스 파일을 읽고 분석한 클래스의 인스턴스 변수, 메서드 코드 등을 Method Area에 저장해요. 클래스 변수도 같이 저장되죠.

물론 모든 코드가 저장되는건 아니에요. new 키워드를 통해 객체가 동적으로 생성되기 전에는 텍스트일 뿐이에요.

객체를 생성하고 메서드를 실행하면 해당 클래스 코드에 대한 정보를 Method Area에 저장하게 되요. 저장되는 목록들을 정리하면 아래와 같아요.

클래스와 인스턴스 정보

보통 클래스와 인스턴스를 통칭하는 걸 Type이라고 해요.

  1. Type의 전체 이름(패키지, 클래스 명)
  2. Type의 직계 하위 클래스 전체 이름
  3. Type의 클래스/인터페이스 여부modifier(public/abstract/final)
  4. 연관된 인터페이스 이름 목록
Runtime Constant Pool

Type의 모든 상수 정보를 가지고 있어요.

  1. Type, Field, Method의 모든 Symbolic Reference 정보를 포함
  2. Constant Pool의 시작점Entry는 배열과 같이 인덱스 번호를 통해 접근
  3. 객체의 접근 등 모든 참조를 위한 핵심 요소
Field Information

Field는 인스턴스 변수를 가리켜요.

  1. Field Type
  2. Field modifier(public/private/protected/static/final/volatile/transient)
Method Information

생성자를 포함한 모든 메서드를 저장해요.

  1. Method 이름
  2. Method 반환 타입
  3. Method 파라미터 수와 Type
  4. Method modifier
  5. Method 구현 부분이 있다면(abstract/native X),
    1. Method의 바이트 코드
    2. Method의 Stack Frame의 Operand Stack 및 지역 변수 섹션section의 크기
    3. 예외 테이블
Class Variable

여기서 클래스 변수는 static 키워드로 선언된 변수를 의미해요.

클래스 변수의 특징을 정리하자면,

  • 모든 인스턴스에 공유되며 인스턴스 없이도 접근이 가능해요.
  • 이 변수는 인스턴스의 것이 아니라 클래스에 속해요.
  • 클래스를 사용하기 이전에 이 변수들은 미리 메모리를 할당 받아 있는 상태가 되요.
  • final 클래스 변수는 상수로 치환되어 Runtime Constant Pool에 값을 복사해요.

Heap

사용자가 관리하는 인스턴스가 생성되는 공간이에요. 객체를 동적으로 생성하면 바로 이 영역의 메모리에 할당되어 사용되요. 프로그램은 시작될 때 미리 이 영역을 많이 할당해 놓으며 인스턴스와 인스턴스 변수를 저장해요.

참조 변수의 경우 Heap에 인스턴스가 저장되는게 아니라 포인터가 저장되요.

참고로 Heap 영역은 JVM이 메모리를 관리하는데 쓰는 가비지 컬렉터Garbage Collector의 대상이 되요.

JVM language Stack

지역 변수와 일부 결과 값을 저장해요. 자바에서 스레드는 생성과 동시에 하나의 JVM Stack을 할당 받아요. 메서드가 호출될 때마다 하나의 프레임이 생성되고, 메서드 내 프로세스가 종료되면 삭제되요.

💡프레임Frame이란?
메서드가 호출될 때마다 만들어지고 메서드의 상태 정보를 저장해요. 총 3가지 구성되어 있어요.
1. 지역 변수
2. Operand Stack \to 메서드 계산을 위한 작업 공간
3. Constant Pool Reference

PC Registers

현재 실행 중인 JVM의 명령을 저장해요. 운영체제에서의 프로그램 계수기Program Counter, PC라 보시면 되요.

각 스레드는 하나의 PC Register를 가져요.

Execution Engine

하드웨어, 소프트웨어 또는 완전한 시스템들을 테스트하는 소프트웨어의 일종이에요.

Native Method Interface

프로그래밍 프레임워크의 일종으로, JVM에서 동작 중인 자바 코드에서 라이브러리와 네이티브 어플리케이션을 호출할 수 있게 해줘요.

:💡네이티브Native의 의미
에뮬레이션이나 호환 모드의 사용 등 외적인 지원 없이, 특정 하드웨어나 OS에서 그대로 실행되는 것을 의미.

Native Method Libraries

Execution Engine을 구동하는데 필요한 C/C++ 네이티브 라이브러리 집합이에요.

자바 컴파일 & 실행 방식

자바는 JVM의 존재 덕분에 크로스 플랫폼Cross-platform 언어라고 했어요. 왜 이렇게 되는지를 자세하게 알아보죠.

그림을 예로 들면, 컴파일러가 a1, a2, a3 자바 파일을 컴파일하면 .class 확장명의 바이트 코드들을 각 파일마다 하나씩 생성해요. 이때 까진 모든 환경(OS, CPU)에서 동일한 결과의 바이트 코드들이 생성되요.

컴파일이 완료되면 JVM이 램RAM에 적재되면서 실행되요. 그리고 JVM의 Class Loader를 통해 바이트 코드들을 RAM에 적재시켜요.

그 다음에 Execution Engine이 바이트 코드들을 네이티브 머신 코드, 즉 해당 플랫폼의 코드(기계어)로 변환을 해요. JVM의 존재 덕분에 자바는 모든 플랫폼에서 동작할 수 있게 되는거에요.

그리고 바이트 코드 컴파일은 즉시Just-in-time,JIT 컴파일 방식을 취하고 있어요.

💡Just-in-timeJIT 컴파일

  • 프로그램을 실행하는 시점에서 필요한 부분을 즉석으로 컴파일하는 방식을 말해요.
  • 매번 같은 코드를 즉석으로 해석하는 대신 처음 실행될 때 해석Interpret를 하면서 자주 쓰이는 코드를 캐싱Caching해서 속도를 개선시켜요.

자바는 컴파일 언어이자 인터프리터 언어에요

프로그래밍 언어를 접하면 각 언어의 수준Level을 정의한 것을 들어보셨을거에요.

High 수준 - C++, Java
중간Middle 수준 - C
Low 수준 - Assembly
마지막 최저Lowest 수준 - 기계어

컴파일러는 고 수준의 언어를 저수준의 언어로 번역해주는 프로그램이에요. 그리고 인터프리터Interpreter는 특정 수준의 언어를 동일한 수준의 다른 언어로 변환을 해주는 프로그램이에요.

자바 컴파일러는 코드를 즉시 기계어로 번역해주는게 아니라 저수준의 파일인 바이트 코드로 번역을 해주죠. 그리고 JVM의 JIT 코드 생성기는 이 바이트 코드를 각 플랫폼에 맞는 기계어로 변환해줘요.

바로 이러한 컴파일 및 인터프리팅 특성을 동시에 가졌기 때문에 자바는 컴파일 언어이자 인터프리터 언어라고 할 수 있어요.

자바가 딴 언어보다 느리다던데... 왜죠?

두 가지 이유 때문에 느려요.

1. Dynamic Linking
C/C++언어와 달리 자바는 번역한 소스들을 연결Link하는 작업을 프로그램이 실행되는 중에실시간, Real time 수행해요.
2. Run-time Interpreter
바이트 코드 변환도 마찬가지 실시간으로 처리해요.

다른 컴파일 언어들은 Link 작업, 소스 코드들을 이미 기계어로 번역하는 작업을 이미 컴파일 과정에서 처리했기 때문에 자바보다 당연히 빠를 수 밖에 없어요.

하지만 이런 언어들은 프로그램을 실행시키는 CPU의 명령어, 기계어로 변환할 수 있어야 하고 운영체제마다 사용할 수 있는 API들을 제공해줘야 하기 때문에 새로운 CPU나 운영체제가 나올 때마다 컴파일러를 만들어줘야 해요. 그래서 이식성이 떨어진다고 볼 수 있어요.

자바는 JVM이 알아서 모든 플랫폼에 대응할 수 있게끔 만들어졌기 때문에 이식성이 높다고 하는거에요. 모든 것이 다 Trade-off가 존재해요 ㅎㅎ.

JVM에 대한 내용은 여기까지에요. 요즘 한창 운영체제랑 컴퓨터 구조 공부한 내용을 정리해서 그런지 전보다 이러한 개념을 이해를 더 하게 되는 거같아요. 기본기의 중요성을 새삼 깨닫게 되네요.

참고

guru99님 블로그
holaxprogramming님 블로그
johngrib님 블로그 - JVM Stack과 Frame

profile
#행복 #도전 #지속성

0개의 댓글