Unicorns

All the things

ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Java Study] - JVM은 무엇이며 자바 코드는 어떻게 실행하는 것인가.
    Java 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

     

    댓글

Designed by Tistory.