iOSminuco
article thumbnail
Published 2021. 8. 12. 08:33
JVM 동작과 실행과정 Android/java
반응형

목표

- 자바소스 파일 JVM 실행 과정 이해

학습할것

- JVM이란 무엇인가

- 컴파일 하는 방법

- 실행하는 방법

- 바이트코드란 무엇인가

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

- JVM 구성요소

- JDK와 JRE 차이

 

1. JVM 이란 무엇인가?

 JVM (Java Virtual Machine) 이란
각 종 OS에 '독립적' 으로 java 프로그램을 작성하여 실행할 수 있게 해주는 구현체이다.

EX) C 언어는 기계어로 컴파일이 되프로 H/W 에 맞게 각각 컴파일이 되어진다.
        - CPU 제조사에 따라 해석할 수 있는 기계어가 다르기 때문이다(C 프로그램은 플랫폼에 종속적).

그에 반에 java 프로그램은 cpu가 해석 할 수 있는 기계어가 아닌 JVM이 해석 가능한 가성 머신용 바이트 코드로 컴파일이 되어 JVM이 OS 의존적인 부분을 대신 처리하고, "자바 바이트코드를 OS에 특화된 코드로 변환" 하기때문에 플랫폰에 "독립적"이게 도작할 수 있다.
       -> JVM은 플랫폰에 종속적
       -> 컴퓨터가 바로 인식할 수 있는 "바이너리 코드"가 아닌 "바이트 코드"로 변환된다.
            - 바이너리 코드란 컴퓨터가 인식할 수 있는 0과 1로 구성된 이진코드를 의미한다.
            - 바이트 코드란 "CPU"가 이해할 수 있는 언어가 바이너리 코드라면, "바이트 코드"는 "가상머신"이 이해할 수 있는 바이너리 코드이다.
              즉, 고급언어로 작상된 "소스코드"를  가상 머신이 이해할 수 있는 중간 코드로 컴파일 되어 어떤 플랫폼에도 종속되지 않게 실행 될 수                  있는 "가상머신용 기계어 코드"이다.
              바이트 코드는 다시 실시간 번역기 또는 저스트 인 타임 (just-in-time, JIT) 컴파일러에 의해 네이티브 코드로 변환된다.
            - 기계어란 "CPU"가 직접 해독할 수 있고 실행할 수 있는 비트단위(0과 1로 이루어짐)로 쓰인 컴퓨터언어
               기계어가 이진코드로 이루어 졌을 뿐이지, 모든 이진코드가 기계어인것은 아니다. (바이너리코드 != 기계어)
                
               * 기계어는 특정 언어가 아니다.
                  - CPU 제조사에서 CPU를 만들 때 CPU에서 사용하는 명령어 집합을 공개하는데, 이것을 기계어라 부른다.
                  - CPU가 변경되면 기계어가 달라진다. (같은 동작을 하는 명령어지만 완전히 다른 0과 1이 나열될 수도 있다.)
                  - 같은 회가의 CPU라도 버전별로 다른 명령을 포함할 수 있으며 다른 회사라도 같은 명령어 집합을 공유할 수도 있다.
           
즉, 소스 파일(*.java)은 컴파일된 자바 클래스 파일(*.clss)을 이해 중간단계 언어(바이트 코드)로 컴파일 되고, JVM이 이러한 컴파일된 자바 클래스파일(*class)을 OS에 맞는 기계어로 변환(인터프리터와 JIT 컴파일러를 통해)하여 실행되는 것이다.
      - JVM 을 위한 바이트 코드를 자바 바이트 코드라고 한다.
      - Javac.exe : 자바 컴파일러, 자바로 작성된 소스코드를 바이트 코드로 변환한다.
      - Java.exe : 자바 인터프리터, 바이트코드를 실행한다.

* 정리
   1) Java 클래스 파일(.class)을 로드하고
   2) 바이트 코드를 해석하며
   3) 메모리 등의 자원을 할당하고 관리하며 정보를 처리하는 프로그램

1 - 1 Java 컴파일 실행 과정 

Java 프로그램이 실행되는 순서

- byte code는 jvm 위에서 os 상관없이 실행된다.

   - jvm은 os에서 독립적이지만 의존적이다.

JVM 실행과정

 


2. 컴파일 하는 방법 and 과정

컴파일 하는 방법

컴파일이란 우리의 언어는 컴푸터가 이해하지 못하므로 컴퓨터가 이해할 수 있도록 "통역"하는 작업을 말한다.

1. 자바 컴파일러는 자바 개발 키트(JDK)에 포함되어 있기 때문에 작성된 코드를 컴파일에 바이트코드를 생성하기 위해선 JDK가 필요하다.
2. 자바 언어 사양(JLS)을 충족하는 자바 소스코드(*.java) 파일을 작석한다.
3. 자바 컴파일러(javac.exe)를 통해 자바 소스코드(.java)를 자바 가상머신 사양(JVM)을 충족하는(.class)로 컴파일한다.
4. *.clss 파일이 생성된 것을 확인 할 수 있다.
    -Test.class

실행하는 방법
java.exe -> 자바 인터프리터로서 컴파일러로 생성된 바이트 코드를 해석하고 실행한다.
- java Test

역컴파일하는 방법
javap.exe(역어셈블러) -> 컴파일된 클래스 파일을 원래의 자바 소스코드로 변환한다.
- javap Test

JVM이 동작하기전에 컴파일러가 하는 일

어휘분석

 - 그림에 보이는 어휘 분석은 public class와 같은 키워드, "Hello World"와 같은 리터럴

    , 그리고 가감승제와 같은 오퍼 에이 터들을 수집한다.

    이런 키워드, 연산자, 리터럴을 '어휘 소'라고 하는데, 모두 수집하면,

    * 변수 : 하나의 값을 저장할 수 있는 저장공간

      상수 :  변하지 않는 변수(단 한 번만 값을 저장할 수 있는 저장공간)

      리터럴 : 데이터를 의미 (그 자체로, 존재 자페만으로 값(데이터)을 의미하는 것)

       ex) final int a = 1 // a는 상수 1은 리터럴이다.

다음과 같은 모습이 나올 것이다. 이렇게 모인 어휘 소를 하나의 스트림으로 만들어 주는데 이를 토큰 스트림이라고 한다.

이렇게 토큰 스트림까지 뽑아내는 과정을 어휘 분석이라고 한다.

 

구문분석 (Syntax Analysis)

 어휘 분석을 통해 토큰 스트림이 나오면 이를 통해서, 문법이 맞는지 확인하는 과정이 필요한다, 이를 구문 분석에서 한다.

만약 에러가 발생하면 Syntax Error가 난다.

 

의미 분석

 대표적으로 타입 검사, 자동 타입 변환 같은걸 진행하게 된다. 구문 분석에서 에러는 못 잡지만 여기서 잡히게 된다. 만약 String에 integer 값을 입력하게 되면 구문분석이 아니라 의미 분석에서 에러가 발생한다.


3. JIT 컴파일러란 무엇인가?

JIT는 Just-In-Time의 약어로서 그림에서 볼 수 있듯이 JRE(실행엔진)안에 존재해서 프로그램을 실제 실행하는 시점에(런타임시)
해당 플랫폼에 맞는 기계어(native machine code)로 컴파일하는 컴파일 기법이다.

즉 간단히 말해서 JRE안에 존재하면서 프로그램을 실행할 때 기계어로 번역해 전달하는 장치인 것이다.

JIT는 왜 쓰이고 어떻게 동작할까?

자바 바이트 코드는 인터프리터 언어(interpreter language)이다. 
인터프리터가 한줄씩 읽고 해석하고, 그에 해당하는 기능을 실행시키는 인터프리터 언어이기에
기기에서 직접 돌아가는 기계어로 컴파일 되는 C/C++와 같은 언어들로 만든 실행 파일에 비하면 실행 속도가 느리다.

이러한 실행 속도를 개선하기 위해 같은 코드를 매번 새롭게 해석하는 대신, 실행하기 전에 미리 JIT 컴파일러를 이용해
"반복적인 코드"를 "네이티브 코드"로 전부 바꿔두고 그 다음부터는 인터프리터가 컴파일된 네이티브 코드를 바로 사용해
인터프리터의 느린 실행 성능을 개선할 수 있다.

단점이라면 JIT 컴파일러가 컴파일하는 과정은 바이트코드를 하나씩 인터프리팅하는 것보다 훨씬 오래 걸리므로 
초기 실행 속도와 메모리 사용량에서 손해를 보는 단점도 있다.
따라서, JIT 컴파일러를 사용하는 JVM들은 내부적으로 해당 메서드가 얼마나 자주 수행되는지 체크하고, 일정 정도를 넘을 때에만 컴파일을 수행한다.

 

(1) 컴파일 방식 : 소스코드를 한꺼번에 컴퓨터가 읽을 수 있는 native machine (기계)어로 변환

(2) 인터프리터 방식 : 소스코드를 빌드시에 암것도 하지 않다가, 런타임시에 한줄 한줄 읽어가며 변환

 

둘 다 장단점이 있는 방식이지만, 여기서 중요한건 자바는 컴파일과 인터프리터 방식을 모두 사용한 다는 것이다. 

그래서 JIT컴파일러를 도입하게 된거 같다, 이미 한번 읽어서 기계어로 변경한, 소스코드는 번역하지 않는다. 더 자세히 보면,

이런식으로 저장소에 저장을 한다는 개념이다. 정확히는 반복되는 코드를 모두 컴파일러로 컴파일 시키는거다. 그래서 인터프리터의 역학을 보조? 해준다고 생각하면 되는데, 인터프리터는 읽을 때, 반복되는 코드로 컴파일된 코드를 바로 사용할 수 있어서 개꿀이다.ㅎㅎ

정확히는 JIT 컴파일러가 "번역 안할래~" 가 아니라, 인터프리터가 안읽어도 된다고 생각하는게 더 옳다. 

 

4.JVM 구성요소

 

JVM은 크게 4가지 구성요소로 이루어져 있다.

1. 클래스 로더 시스템
    - 우리가 컴파일한 바이트코드(*.class)를 실행시점(RunTime)에 읽어들여서 메모리(Runtime Data Area)에 적절하게 배치하는 것이 클래스로더의 역할이다.
    - 크게 3가지 로딩 -> 링크 -> 초기화의 순서로 일을 한다.
        - 로딩 : 클래스 로더가 .class 파일을 읽고 그 내용에 따라 적절한 바이너리 데이터를 만들고 메서드 영역에 저장
                로딩이 끝나면 해당 클래스 타입의 Class 객체를 생성하여 “힙" 영역에 저장.
        - 링크 : Verify(확인), Prepare(준비), Resolve(해결) 세 단계로 나누어져 있다.
        - 초기화 : static 변수의 값을 할당한다. static 블럭은 이때 실행된다.
    
2. 메모리(Runtime Data Areas)
    - JVM이 프로세스로써 수행되기 위해 OS로부터 할당받는 메모리 영역이다. 목적에 따라 크게 5가지 블럭으로 나뉘어있다.
    - 메소드 영역 : 클래스 수준의 정보 (클래스 이름, 부모 클래스 이름, 메소드, 변수) 저장.
    - 힙 영역 : 객체를 저장한다. 인스턴스들이 다 힙에 저장된다.
    - 스택 영역 : 메소드가 호출될 때마다 스택 프레임이라 불리는 블럭이 하나씩 생성되고 메소드 실행이 완료되면 삭제된다.
    - PC 영역 : 쓰레드 내 현재 실행할 스택 프레임을 가리키는 포인터가 생성된다.
    - 네이티브 메소드 영역 : 다른 언어(C, C++)의 메소드 호출을 위해 할당되는 구역, 언어에 맞게 Stack이 생성된다.
    
    - 메소드 영역, 힙 영역 : 여기에 저장된 정보들은 모든 Thread 공유
    - 스택 영역, PC 영역, 네이티브 메소드 영역 : 쓰레드 마다 생성되어 저장된 정보를 공유하지 않는다.
    
3. 실행 엔진
    - Class Loader를 통해 JVM 내의 Runtime Data Areas에 배치된 바이트코드를 명령어 단위로 읽어서 실행한다.
    - 두가지 방식의 조합을 통해 실행하는데
        - JIT 컴파일러 : 인터프리터의 단점을 보완하기 위해 도입, 위에서 설명
        - 인터프리터 : 바이트코드 명령어를 하나씩 읽어서 해석하고 실행한다. 이 과정에서 바이트코드가 네이티브 코드로 변환된다.
    - GC : 실행엔진의 제일 중요한 부분으로 더이상 참조되지 않는 객체를 모아서 정리한다.

4-1. JNI(Java Native Interface)
    - 자바 애플리케이션에서 C, C++, 어셈블리로 작성된 Native 키워드를 사용한 함수를 사용할 수 있는 방법 제공
4-2. 네이티브 메소드 라이브러리
    - C, C++로 작성 된 라이브러리

5. JRK와 JRE 차이

JDK, JRE, JVM 요약

JDK JRE JVM
jdk는 개발자 키트로, 개발에 필요한 어플리케이션 소프트웨어이다.  소프트웨어 번들로, jre는 자바 클래스와 라이브러리, 필수적인 컴포넌트들을 제공한다. jvm은 바이트코드를 실행하고, 실행에 필요한 환경을 제공한다.
jdk는 플랫폼에 독립적이다. jre는 플랫폼에 독립적이다. jvm은 플랫폼에 종속적이다.

JRE란

JDK(Java Development kit)란 자바 애플리케이션 개발 환경으로 
JRE(자바 실행 환경 / JVM) + 소스 파일의 컴파일러 및 디버거 등 자바 애플리케이션을 개발하기 위한 도구(javac, java등)가 포함되어 있다.

즉 JDK를 설치하면 JRE도 같이 설치가 되기 때문에, JDK = JRE + @ 이다.

정리

자바 소스파일을 Java Complier 가 바이트 코드(*.class)로 변환하고, Class Loader 가 Runtime Data Area 에 클래스 파일을 적재 시킨다.
Execution Engine 이 자바 메모리에 적재된 클래스 들을 기계어로 변환해 명령어 단위로 실행하고
Garbage Collector 는 Heap 영역에 적재된 객체들 중에서 참조되지 않은 객체를 제거한다.
반응형
profile

iOSminuco

@minuco

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!