Java

[Java Study] - JVM은 무엇이며 자바 코드는 어떻게 실행하는 것인가.

connieya 2021. 9. 16. 19:52

JVM이란 무엇인가

JVM (Java Virtual Machine) : 자바 프로그램이 실행되는 가상 컴퓨터(VM)

운영체제에서 바로 실행하면 되는 데 JVM 이라고 하는 가상 머신(기계)이 왜 필요 한 것인가 ??

 

  • 자바 프로그램은 바이트 코드 이기 때문에 운영체제가 이것을 해석하고 실행 할 수 없다.
  • 자바 가상 머신으로 자바 바이트 코드(.class 파일)를 OS에 특화된 코드로 변환(인터프리터와 JIT 컴파일러)하여 실행한다.
  •  JVM이 자바를 실행하는 가상의 운영체제 역할을 담당 한다.
  •  즉 , JVM은 운영체제와 자바 프로그램를 연결 해주는 중간 다리라고 보면 된다.
  •  그렇기 때문에 운영체제에 맞는 JVM을 설치하기만 하면 모든 운영체제에서 자바를 실행 할 수 있다.

자바는 운영체제에 독립적, JVM은 운영체제에 종속적이다.

컴파일 하는 방법

자바 프로그램에서 컴파일이란?

  •   JVM은 java code를 이해하지 못한다.
  •    java code(텍스트 파일)을 JVM이 사용할 수 있게 class file(.class)를 생성하는 것을 말한다.

 

 

1.자바 설치(JDK)

  •  JDK 폴더 안에는 javac.exe 라는 자바 컴파일러가 포함 되어 있다.

 

자바 컴파일러(Java compiler)

자바를 가지고 작성한 자바 소스 코드를 자바 가상 머신이 이해할 수 있는 자바 바이트 코드로 변환해준다.

자바 컴파일러는 자바를 설치하면 javac.exe 라는 실행 파일 형태로 설치된다. 

 

 

2. java 소스 파일 작성하기

public class Hello {  
public static void main(String\[\] args) {  
System.out.println("hello world");  
}  
}  

 

3. 자바 컴파일러 실행

  •  Hello.java 컴파일
  •  명령 프롬프트 실행 -> 소스 파일이 있는 경로로 이동(cd 파일경로)

 

 

  •  javac.exe (.exe 생략 가능) -> javac Hello.java 입력
  •  바이트 코드 Hello.class 가 생성됨

 

 

 

실행하는 방법

JDK 폴더 안에 자바를 실행할 수 있는 java.exe가 있다.

  • 명령 프롬프트 실행 -> 컴파일된 파일 경로로 이동
  • java.exe로 자바 실행
  • java Hello 입력 (바이트 코드 파일을 실행할 때 .class 확장명을 제외하고 입력해야 함)

 

 

  • JVM이 바이트 코드 파일을 메모리에 로드하고 , 기계어로 번역한다.
  • java main() 메소드를 찾아 실행한다.

 

바이트코드란 무엇인가

JVM(자바 가상 머신) 이 이해할 수 있는 언어로 변환된 코드를 의미

  • Java Code(소스 파일)를 컴파일 한 결과로 생성된 코드이다.
  • JVM이 이해할 수 있는 언어이다.
  • Java Code 와 Machine Code 의 중간 코드이다.
  • 자바 컴파일러에 의해 변환되는 코드의 명령어 크기가 1바이트라서 자바 바이트 코드라고 불린다.
  • 확장자는 .class이다.
  • JVM만 설치되어 있으면, 어떤 운영체제에서라도 실행될  수 있다.

javap 

javap 는 .class 파일의 필드, 생성자, 메서드들의 정보를 보여주는 명령어이다. 

 

Hello.java

public class Hello {
    public static void main(String[] args) {
        System.out.println("hello , java");
    }
}

 

javap :  최소 구성만 보여준다.

 

javap Hello.class

Compiled from "Hello.java"
public class step1.Hello {
  public step1.Hello();
  public static void main(java.lang.String[]);
}

 

javap -p :  모든 클래스와 멤버를 보여준다.

 

javap -p Hello.class

Compiled from "Hello.java"
public class step1.Hello {
  public step1.Hello();
  public static void main(java.lang.String[]);
}

 

javap -v :  stack size , arguments for methods 와 같은 자세한 정보를 보여준다.

 

javap -v Hello.class

Last modified 2021. 10. 12.; size 422 bytes
  MD5 checksum ff4146e1163a7cb1070c19e462ccf9f6
  Compiled from "Hello.java"
public class step1.Hello
  minor version: 0
  major version: 55
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #5                          // step1/Hello
  super_class: #6                         // java/lang/Object
  interfaces: 0, fields: 0, methods: 2, attributes: 1
Constant pool:
   #1 = Methodref          #6.#15         // java/lang/Object."<init>":()V
   #2 = Fieldref           #16.#17        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #18            // hello , java
   #4 = Methodref          #19.#20        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #21            // step1/Hello
   #6 = Class              #22            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               main
  #12 = Utf8               ([Ljava/lang/String;)V
  #13 = Utf8               SourceFile
  #14 = Utf8               Hello.java
  #15 = NameAndType        #7:#8          // "<init>":()V
  #16 = Class              #23            // java/lang/System
  #17 = NameAndType        #24:#25        // out:Ljava/io/PrintStream;
  #18 = Utf8               hello , java
  #19 = Class              #26            // java/io/PrintStream
  #20 = NameAndType        #27:#28        // println:(Ljava/lang/String;)V
  #21 = Utf8               step1/Hello
  #22 = Utf8               java/lang/Object
  #23 = Utf8               java/lang/System
  #24 = Utf8               out
  #25 = Utf8               Ljava/io/PrintStream;
  #26 = Utf8               java/io/PrintStream
  #27 = Utf8               println
  #28 = Utf8               (Ljava/lang/String;)V
{
  public step1.Hello();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String hello , java
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 5: 0
        line 6: 8
}

 

 

javap -c :  .class 파일을 분해해서 해석해준다.

 

javap -c  Hello.class 

Compiled from "Hello.java"
public class step1.Hello {
  public step1.Hello();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #3                  // String hello , java
       5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
}

 

 

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

 

bytecode를 기계어로 변환하기 위해 JVM Execution Engine(실행 엔진)에 있는 Interpreter 와 JIT 컴파일러가 구동된다.

Interpreter가 매번 바이트 코드를 읽고 한 줄 씩 기계어 코드를 생성하고 이를 순차적으로 실행한다.
심지어 같은 메서드를 여러번 호출 할 때, 매번 한 줄씩 interpreter 가 구동된다.

이로 인해 시스템 효율과 성능이 떨어질 수 있다.

 

JIT(Just-In-Time) 컴파일러가 이 점을 보완 해 줄 수 있다.
인터프리터 방식으로 기계어를 생성할 때 자주 사용되는 메서드, 변수 그리고 여러번 호출 되는 메서드를 캐싱하여
재사용 할 수 있다. 이는 매번 기계어 코드를 생성하는 것을 방지 할 수 있다.

 

 

요약하면

  • JIT 컴파일러는 자바 컴파일러(javac.exe)와 다른 별개의 컴파일러이다.
  • interpreter 방식 과 정적 compile 방식을 혼합한 방식이다.
  • JVM의 구성 요소 중 하나이다.
  • 바이트 코드를 기계어로 컴파일 할 때 사용
  • 실행 과정에서 컴파일 하는 방식이다. (동적 컴파일 방식)

JVM 구성 요소

바이트 코드를 읽고 자바를 실행 하는 JVM의 구성 요소를 살펴보자

  • Class Loader
    • 컴파일 된 .class file을 읽은 뒤 Method Area에 저장한다.
    • 로딩 : 클래스를 읽어 오는 과정
    • 링크 : 레퍼런스를 연결하는 과정
    • 초기화 : static 값들 초기화 및 변수에 할당
  • Runtime Data Area : JVM이 Java Bytecode를 실행하기 위해 사용하는 메모리 공간
    • method area : 클래스 로더가 클래스 파일을 읽어오면 클래스 정보(runtime constant pool , field , method data , code for methods)
      를 파싱해서 저장하는 곳
    • heap : 프로그램을 실행하면 생성한 모든 객체를 저장하는 곳
    • pc registers : 각 스레드가 메서드를 실행하고 있을 때, 메서드 안에 몇 번째 줄을 실행해야 하는지 나타내는 역할
    • stack : 스레드 별로 1개만 존재한다. 스택 프레임은 메서드가 호출될 때 마다 생성된다. 메서드 실행이 끝나면 스택 프레임은 pop되어 제거된다.
  • native method stacks : Java Bytecode가 아닌 다른 언어로 작성된 메서드를 의미
  • Execution Engine : 바이트코드를 실행 하는 곳
    • Interpreter : 바이트코드를 한 줄 씩 읽어서 기계어로 변환한다. 한 줄 씩 읽어서 변환 하기 때문에 느릴 수 있다.
    • JIT Compiler : 위에서 언급한 interpreter의 단점을 보완해 줄 수 있다.
      코드 중에 반복되거나 자주 실행되는 코드를 기계어로 컴파일 한 뒤 캐싱해서 재사용한다.
    • Garbage Collector : JVM의 Heap 영역에서 사용하지 않는 객체를 삭제하는 프로세스를 말한다.
  • Native Method Interface(JNI)
    • 네이티브 응용 프로그램 , C, C++와 같은 다른 언어로 작성된 어플리케이션과 연동할 수 있는 인터페이스를 제공
    • 네이티브 응용 프로그램, C, C++ 라이브러리를 호출 할 수 있게 해줌
    • 또한 네이티브 응용 프로그램, C,C++ 라이브러리가 호출 할 수 도 있게 해주는 역할도 수행

 ex)

 public static void main(String[] args) {
        Thread.currentThread();
  }

Thread.currentThread(); 는 C로 구현한 메서드이다.  앞에 native 키워드가 있다.

 

 

  • Native Method Libraries
    • 네이티브 응용 프로그램 , C, C++ 와 같은 다른 언어들의 라이브러리를 의미

※ 네이티브 응용 프로그램 이란?

하드웨어와 운영 체제 플랫폼에 종속된 프로그램들을 말한다

.

JDK 와 JRE 의 차이

  • JRE (Java Runtime Environment)
    • 자바 실행 환경
    • JVM이 자바 프로그램을 동작시킬 때 필요한 라이브러리 파일들과 기타 파일들을 가지고 있다.
    • 즉, JVM의 실행환경을 구현해놓음
    • 구성 : JVM + 표준 라이브러리(Java 2D, AWT, Swing ,CORBA , JDBC, JNDI .. 등)
    • 이미 개발 된 프로그램을 실행만 할 경우
  • JDK (Java Development Kit)
    • 자바 개발도구
    • 구성 : JRE + 개발에 필요한 도구(컴파일러 ,javadoc, java 등)
    • 실행 뿐 아니라 자바 프로그램을 개발하고 싶을 때
    • 오라클은 자바 11부터는 JDK만 제공하며 JRE를 따로 제공하지 않는다.

 

 

REFERENCE

 

 

이것이 자바다
자바의정석
더 자바, 코드를 조작하는 방법
https://www.theserverside.com/definition/Java-compiler
http://www.tcpschool.com/java/java_intro_programming
https://www.tutorialspoint.com/How-to-compile-a-java-program
https://wikidocs.net/257
https://www.ibm.com/cloud/learn/jre
https://stackoverflow.com/questions/1906445/what-is-the-difference-between-jdk-and-jre
https://www.softwaretestinghelp.com/java-components-java-platform-jdk/
http://javapracs.blogspot.com/2011/12/one-java-2-compilers-javac-and-jit.html
https://www.baeldung.com/java-class-view-bytecode