Language/JAVA

1주차 과제 : JVM은 무엇이며 자바 코드는 어떻게 실행하는 것인가.

정리

JVM(Java Virtual Machine)은 자바 바이트코드를 실행하기 위한 가상 머신입니다.
JVM의 구조와 동작 과정에 대해 상세히 설명해보겠습니다.

JVM의 구조

JVM은 크게 세 가지로 구성됩니다.

Class Loader
클래스 로더는 클래스 파일(.class)을 읽어들여 메모리에 적재합니다.
로딩, 링크, 초기화의 과정을 거쳐 클래스를 실행합니다.

Runtime Data Area
JVM이 프로그램을 실행하기 위해 사용하는 메모리 영역입니다.
Method Area, Heap, Stack, PC Register, Native Method Stack으로 구성됩니다.

Execution Engine
실행 엔진은 로드된 클래스 파일을 바이트 코드 단위로 읽어들여 실행합니다.
인터프리터 방식과 JIT(Just-In-Time) 컴파일러 방식이 있습니다.


JVM의 동작 과정

클래스 로더가 클래스 파일을 읽어들입니다.
클래스 파일은 Method Area에 저장됩니다.
클래스 파일의 코드 영역은 Execution Engine에 의해 실행됩니다.
프로그램이 실행되면서 Heap에 객체가 생성됩니다.
Stack에 메소드 호출 정보가 저장됩니다.
Native Method Stack에 JNI(Java Native Interface)를 사용하는 네이티브 메소드가 호출됩니다.
PC Register에 다음 실행할 코드의 주소를 저장합니다.
인터프리터나 JIT 컴파일러에 의해 바이트 코드가 실행됩니다.
실행이 완료되면 결과값을 Stack에 저장하고, PC Register에 다음 실행할 코드의 주소를 저장합니다.
실행 중 예외가 발생하면 예외 처리기가 처리합니다.
프로그램이 종료되면 Heap과 Method Area에 사용된 메모리를 해제합니다.

JVM은 자바 코드를 운영체제와 하드웨어에 맞게 변환하여 실행하기 때문에, 운영체제나 하드웨어의 종류와 상관없이 자바 프로그램을 실행할 수 있습니다. 이러한 특징으로 인해 자바는 플랫폼 독립성을 가지고 있습니다.

백기선님 주최 자바 스터디가 있어서 늦게나마 참여해보려 합니다.

스터디 링크 :

github.com/whiteship/live-study/issues/1

목표

자바 소스 파일(.java)을 JVM으로 실행하는 과정 이해하기.

학습할 것

  • JVM이란 무엇인가
  • 컴파일 하는 방법
  • 실행하는 방법
  • 바이트코드란 무엇인가
  • JIT 컴파일러란 무엇이며 어떻게 동작하는지
  • JVM 구성 요소
  • JDK와 JRE의 차이

 

1. JVM이란 무엇인가?

 

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

자바 프로그램은 완전한 기계어가 아닌, 중간 단계의 바이트 코드이다.

때문에 이것을 해석하고 실행할 수 있는 가상의 운영체제가 필요하다. 이것이 JVM이다.

바이트코드를 실행할 수 있는 주체이다.

모든 JVM은 JVM규격에 맞춰 바이트 코드를 실행한다.

표준 자바 API까지 동일 동작을 하도록 구현한 상태에선 이론상 모든 자바 프로그램은 CPU와 OS 종류에 무관하게 동일 동작을 보장한다.

플랫폼에 독립적이라고 할 수 있다. (write once run anywhere)

 

 

JVM의 특성 

 

  • 스택 기반의 가상 머신
  • 단일 상속 형태의 객체 지향 프로그래밍을 가상 머신 수준에서 구현
  • 포인터를 지원하되 C와 같이 주소 값을 임의로 조작이 가능한 포인터 연산이 불가능
  • 가비지 컬렉션 사용
  • 모든 기본 타입의 정의를 명확히 함으로써 플랫폼 독립성 보장
  • 데이터 흐름 분석(data flow analysis)에 기반한 자바 바이트코드 검증기 (verifier)를 통해 스택 넘침, 명령어 피연산자의 타입 규칙 위반, 필드 접근 규칙 위반, 지역 변수의 초기화 전 사용 등 많은 문제를 실행 전에 검증하여 실행 시 안전을 보장하고 별도의 부담을 줄여줌
  • 명령어에서 스택에서 가져올 피연산자의 타입을 명령어에 지정(예: 정수 덧셈은 iadd, 단정밀도 실수 덧셈은 fadd)

 

2. 컴파일 하는 방법

 

컴파일이란?

소스 코드 (원시 코드)에서 목적 코드로 옮기는 과정을 컴파일(Compile)이라고 한다.

소스 코드를 컴파일하는 이유는 대부분 사람에게 이해하기 쉬운 형태의 고수준 언어(Java, C, C++로부터 실행가능한 기계어 프로그램을 만들기 위해서이다. 좁은 의미의 컴파일러는 주로 고수준 언어로 쓰인 소스 코드를 저수준 언어(어셈블리어기계어 등)로 번역하는 프로그램을 가리킨다.

 

자바에서의 컴파일

가상 머신에서 동작할 프로그램을 만드는 컴파일러도 있다. 이 경우 일반적으로 출력물이 가상 머신을 위해 제작된 바이트코드 형태의 기계어가 되므로 바이트코드 컴파일러라고 부르는데 자바가 이에 해당한다.

 

자바에서 컴파일(Compile)을 하려면 .java(자바 소스 파일)을 Java Compiler가 .class파일(ByteCode)로 로 변환하는 것을 의미한다.

 

컴파일 예제

    1.

 

메모장을 실행하고 C:\Temp 디렉토리에 Hello.java라고 저장하자

 

    2.

 

간단하게 문자열을 출력하는 코드를 기입해보자.

 

    3.

 

C드라이브의 Temp폴더로 이동 후 파일이 있는지 확인하고 javac Hello.java(자바 소스파일) 명령어로 컴파일

 

 

3.실행하는 방법

 

Hello.class를 실행하기 위해 JVM 구동 명령어인 java.exe를 다음과 같이 입력하고(java Hello) Enter키를 눌러 실행

 

4.바이트코드란 무엇인가

 

바이트코드(Bytecode, portable code, p-code)는 특정 하드웨어가 아닌 가상 컴퓨터에서 돌아가는 실행 프로그램을 위한 이진 표현법이다. 하드웨어가 아닌 소프트웨어에 의해 처리되기 때문에, 보통 기계어보다 더 추상적이다.

 

역사적으로 바이트코드는 대부분의 명령 집합이 0개 이상의 매개 변수를 갖는 1바이트 크기의 명령 코드(opcode)였기 때문에 바이트코드라 불리게 되었다. 바이트코드는 특정 하드웨어에 대한 의존성을 줄이고, 인터프리팅도 쉬운 결과물을 생성하고자 하는 프로그래밍 언어에 의해, 출력 코드의 한 형태로 사용된다. 컴파일되어 만들어진 바이트코드는 특정 하드웨어의 기계 코드를 만드는 컴파일러의 입력으로 사용되거나, 가상 컴퓨터에서 바로 실행된다.

 

사람이 읽기 쉽도록 쓰인 소스 코드와 비교하면, 바이트 코드는 덜 추상적이며, 더 간결하고, 더 컴퓨터 중심적이다. 예를 들어 바이트코드는 변수의 접근 범위(지역변수 또는 전역변수 인지 여부) 등과 같은 의미 분석 단계의 결과를 부호화한다. 그래서 일반적으로 소스 코드를 직접 분석/실행하는 것보다 더 좋은 성능을 보여 준다.

 

바이트코드 프로그램은 보통 한 번에 하나의 명령어를 읽은 후 실행한다. 이러한 형태의 바이트코드 인터프리터는 높은 이식성을 갖는다. 또 다른 형태로서 실시간 번역기 또는 저스트 인 타임(just-in-time, JIT) 컴파일러라 불리는 시스템은 실행 중에 필요에 따라서 바이트코드를 기계어로 번역한다. 이 방법은 가상 컴퓨터의 이식성을 없애지만, 바이트코드 자체에 대한 이식성을 잃지는 않는다. 예를 들어, 자바와 C# 코드는 보통 바이트코드 형태로 컴파일되어 저장되며, 실행 전에 JIT 컴파일러에 의해 기계 코드로 번역된다. 이 번역으로 인해 프로그램 실행 전에 지연시간이 발생하게 되지만, 보통 인터프리터보다는 훨씬 좋은 성능을 보여 준다.

 

5.JIT 컴파일러란 무엇이며 어떻게 동작하는지

 

JIT 컴파일(just-in-time compilation) 또는 동적 번역(dynamic translation)은 프로그램을 실제 실행하는 시점에 기계어로 번역하는 컴파일 기법이다.

 

전통적인 입장에서 컴퓨터 프로그램을 만드는 방법은 두 가지가 있는데, 인터프리트 방식과 정적 컴파일 방식으로 나눌 수 있다. 이 중 인터프리트 방식은 실행 중 프로그래밍 언어를 읽어가면서 해당 기능에 대응하는 기계어 코드를 실행하며, 반면 정적 컴파일은 실행하기 전에 프로그램 코드를 기계어로 번역한다.

 

JIT 컴파일러두 가지의 방식을 혼합한 방식으로 생각할 수 있는데, 실행 시점에서 인터프리트 방식으로 기계어 코드를 생성하면서 그 코드를 캐싱하여, 같은 함수가 여러 번 불릴 때 매번 기계어 코드를 생성하는 것을 방지한다.

 

JIT(just-in-time) Compiler

JIT 컴파일러는 바이트 코드를 읽어 빠른 속도로 기계어를 생성할 수 있다. 이런 기계어 변환이 코드가 실행되는 과정에 실시간으로 일어나며 전체 코드의 필요한 부분만 변환한다. 기계어로 변환된 코드는 캐시에 저장되기 때문에 재사용시 컴파일을 다시 할 필요가 없다. 여기서 기계어 변환이 실시간으로 일어나서 Just in time이라는 명칭이 붙었다고 한다.

 

더 많은 내용은 아래 링크를 참조하자.

aboullaite.me/understanding-jit-compiler-just-in-time-compiler/

 

Understanding JIT compiler (just-in-time compiler)

he Just-In-Time (JIT) compiler is a component of the Java Runtime Environment that improves the performance of Java applications at run time. Nothing in the JVM affects performance more than the compiler, and choosing a compiler is one of the first decisio

aboullaite.me

 

6.JVM 구성 요소

 

JVM 은 크게 4가지로 구분됩니다.

 

  • Class Loader
  • Execution Engine
  • Runtime Data Area = memory
  • Native

그리고 Runtime Data Area 는 5가지로 구성됩니다.

  • Method Area (= Class Area, Code Area, Static Area)
  • Heap Area
  • Stack Area
  • PC registers
  • Native Method stack

 

JVM의 4가지 구성 요소 및 각 구성의 세부내용

 

 

JVM 을 구성하는 각 요소들에 대해서 알아봅시다.

 

Class Loader

 

클래스 로더의 구성요소

 

 

클래스 로더는 로딩, 링크, 초기화 순서로 진행됩니다. 클래스 로더라는 이름 그대로 클래스 파일을 적재(Runtime Data Area) 하는 역할을 합니다.

Event.java 자바 소스 파일을 컴파일 하면 바이트 코드(.class 파일)이 생성되며 이런 파일들을 모아, 클래스 로더가 메모리에 적재시킵니다.

  • 로딩 : 클래스 로더가 .class 파일을 읽고 그 내용에 따라 적절한 바이너리 데이터를 만들고 메서드 영역에 저장 (메서드 영역에 저장하는 데이터는 FQCN(Full Qualified Class Name), 클래스, 인터페이스 , Enum, 메서드와 변수) 로딩이 끝나면 해당 클래스 타입의 Class 객체를 생성하여 힙 영역에 저장합니다.
  • 링크 : Verify, Prepare, Resolve 세 단계로 나누어져 있습니다. 검증(verify) 은 .class 파일 형식이 유효한지 체크합니다. Preparation 은 클래스 변수와 기본값에 필요한 메모리, Resolve 는 심볼릭 메모리 래퍼런스를 메서드 영역에 있는 실제 레퍼런스로 교체합니다.
  • 초기화 : static 변수의 값을 할당합니다. static 블럭은 이때 실행됩니다.

클래스 로더는 기본적으로 세 가지 클래스 로더가 제공됩니다.

  • BootStrap ClassLoader : JAVA_HOME\lib 에 있는 코어 자바 API 를 제공합니다. 최상위 우선순위를 가진 클래스 로더. Native 로 구현됨.
  • Platform ClassLoader : JAVA_HOME\lib\ext 폴더 또는 java.ext.dirs 시스템 변수에 해당하는 위치에 있는 클래스를 읽습니다.
  • Application ClassLoader : 애플리케이션 클래스패스(애플리케이션 실행할 때 주는 -classpath 옵션 또는 java.class.path 환경 변수의 값에 해당하는 위치)에서 클래스를 읽습니다.

 

/**
* maven archetype-quickstart proejct
*/
public class App {
    public static void main(String[] args) {
      // ClassLoader 는 계층형이라 부모가 존재한다.
      ClassLoader classLoader = App.class.getClassLoader();
      
      /**
      * AppClassLoader : classLoader.getParent() 
      * PlatformClassLoader : classLoader.getParent().getParent()
      */
    }
}

 

Execution Engine

 

Execution Engine 은 Class Loader에 의해 Runtime Data Area 에 적재된 클래스(바이트 코드)들을 컴퓨터가 이해할 수 있는 기계어로 변경해 명령어 단위로 실행하는 역할을 합니다. 이때 명령어를 하나씩 실행하는 인터프리터 방식이 있고, 바이트 코드를 네이티브 코드로 변환하는 JIT Compiler 방식이 있습니다.

 

RunTime Data Area

클래스 파일들이 적재되어있는 장소인 Runtime Data Area 에는 5 가지 영역이 존재합니다.

  • 메서드 영역(Method Area) : 클래스 멤버 변수 이름, 데이터 타입, 리턴타입, 상수풀, static 변수 등이 저장됩니다. 클래스 수준의 정보가 저장됩니다. 여기에 저장된 정보들은 공유 됩니다.
  • 힙 영역(Heap Area) : new 연산자로 생성된 객체와 배열이 저장됩니다. ex) Event event = new Event(); 로 생성한 event 는 스택영역에 저장되며 new 연산자로 생성한 Event() 가 힙 영역에 저장됩니다. event 변수는 힙 영역의 주소값을 가지고 있습니다. 여기에 저장된 정보들은 공유 됩니다.
  • 스택 영역(Stack Area) : 스레드가 생성될 때 마다 생성되는 영역으로, 대표적으로 지역변수가 여기에 저장됩니다. 메서드를 호출할 때마다 스택이 개별적으로 생성됩니다. 따라서 동시성(concurrency) 문제를 해결하기 위해서 메서드에 동기화 블럭(synchronized) 를 지정하는데, 동기화 블럭 없이 동시성 문제를 해결하는 방법중 하나가, 지역 변수를 이용하는 방법이 있습니다.
  • PC Register : 쓰레드가 생성될 때마다 생성되는 영역으로 Program Counter 즉, 현재 쓰레드가 실행되는 부분의 주소와 명령을 저장하고 있는 영역입니다. 이것을 이용해서 쓰레드를 돌아가면서 수행할 수 있게 합니다. 쓰레드가 생성되었을때 메서드와 힙 영역은 모든 스레드가 공유하며, PC Register, 스택 영역, Native Stack Area 는 공유되지 않습니다.
  • Native Method Stack : 자바 언어 이외의 언어로 작성된 코드를 저장하는 메모리 영역입니다. 주로 native 키워드가 붙은 애들이 저장됩니다.
// C 언어로 구현된 currentThread
public static native Thread currentThread();

이러한 메서드들은 JNI(Java Native Interface) 를 이용하여 자바 애플리케이션에서 C, C++ 등으로 작성된 함수들을 native 키워드를 사용하여 메서드 호출을 할 수 있습니다.

 

Garbage Collector

가비지 컬렉터는 힙 영역에 생성된 객체들 중에 참조되지 않는 객체들을 메모리에서 제거하는 역할을 합니다. 가비지 컬렉터가 역할을 하는 시간은 정확히 언제인지를 알 수 없습니다. (참조가 없어지자마자 해제되는 것을 보장하지 않음) 또 다른 특징은 가비지 컬렉터가 수행되는 동안 가비지 컬렉터를 수행하는 쓰레드가 아닌 다른 모든 쓰레드가 일시정지됩니다.

 

7.JDK와 JRE의 차이

 

 

JDK, JRE, JVM을 나타내는 사진

 

 


출처 :

 

자바 가상 머신 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 자바 가상 머신 사양의 자바 SE 7 에디션에 기반을 둔 자바 가상 머신(JVM) 아키텍처의 개요도. 자바 가상 머신(영어: Java Virtual Machine, JVM)은 자바 바이트코드를 실

ko.wikipedia.org

 

 

컴파일러 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 컴파일은 여기로 연결됩니다. 일본의 기업에 대해서는 컴파일 (기업) 문서를 참조하십시오. 일반적인 다중 언어, 다중 대상 컴파일러의 동작 모습. 컴파일러(com

ko.wikipedia.org

 

JVM(Java Virtual Machine) Architecture

JVM 은 자바 가상 머신으로 자바 바이트 코드(.class 파일)를 OS에 특화된 코드로 변환(인터프리터와 JIT 컴파일러)하여 실행합니다.

medium.com