스터디/LiveStudy

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

TheWing 2020. 11. 20. 18:34

본 게시글은 백기선님의 자바 라이브 스터디를 진행하기 위한 정리 자료입니다

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

JVM이란 무엇인가

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

  • 자바 바이트 코드를 실행시키기 위한 가상 머신이라고 할 수 있다.
  • 자바로 작성된 모든 프로그램은 JVM에서만 실행이 가능하고 자바 프로그램을 실행하기 위해서는 반드시 JVM이 있어야한다
  • C 프로그램은 기계어로 컴파일하고 하드웨어 기종에 맞게 각각 컴파일 되어있어야 한다. 즉 '플랫폼에 종속적'이다. Java 같은 경우 JVM만 설치 되어있으면 기기의 기종에 상관없이 사용이 가능하며 한번만 컴파일이 가능하고 플랫폼에 독립적이다.

JVM 구성

클래스 로더 (class loader)

  • JVM 내로 .class 파일들을 load하고 로딩된 class들은 runtime data area에 배치된다. (로딩)

  • 자바는 동적으로 클래스를 읽어오고 프로그램이 실행 중일때 런타임에서 모든 코드들이 JVM에 연결된다 (링크)

  • 클래스에 있는 static 값을 초기화한다 (초기화)
  • 로딩 방법에는 두가지의 로딩 방법이 있다. 런타임 동적로딩과 로드타임 동적 로딩이 있다.

    • 런타임 동적 로딩(Run-time dynamic loading)

        public class HelloWorld {
            public static void main(String[] args) {
                Class c = Class.forName(args[0]);
            }
        }

      HelloWorld 클래스를 실행하였다고 가정하겠다.
      객체를 참조하는 순간에 동적으로 Loading 하는 방식이다. 위 코드에서, Class.forName(args[0])은 파리미터로 받은 (args[0]에 해당하는 클래스를 로딩한 후에, 그 클래스에 해당하는 Class 인스턴스를 리턴하게 된다. Class.forName() 메소드가 실행되기 전까지는 HelloWorld 클래스에서 어떤 클래스를 참조하는지는 알 수 없고 이것을 알려면 인자로 넘어오는 args[0]가 인수로 넘어온 후에나 알 수 있다. 이처럼 클래스를 로딩할 떄가 아닌 코드를 실행하는 순간에 클래스를 로딩하는 것을 런타임 동적 로딩이라고 한다.

    • 로드타임 동적 로딩(load-time dynamic loading)

        public class HelloWorld {
            public static void main(String[] args) {
                System.out.println("Hello World!!!");
            }
        }

      HelloWorld 클래스를 실행하였다고 가정하겠다.
      부트스트랩 클래스로더가 생성된 후에 모든 클래스가 상속 받고 있는 Object클래스를 읽어오고 그 후에 클래스로더는 명령행에서 지정한 HelloWorld 클래스를 로딩하기 위해서, 컴파일된 HelloWorld.class 파일을 읽는다. HelloWorld 클래스를 로딩하는 과정에서 필요한 클래스가 존재하는데 현재 예시에서는 java.lang.Stringjava.lang.System 클래스이다. 이 두 클래스는 HelloWorld 클래스를 읽어오는 과정에서 로드타임에 로딩이 된다.

      이렇게 하나의 클래스를 로딩하는 과정에서 동적으로 클래스를 로딩하는 것을 로드타임 동적 로딩이라고 한다.
      간략하게 순서를 나열해보면 Object Class를 읽는다 ->HelloWorld 클래스를 Loading 하기 위해서 HelloWorld.class 파일을 읽는다 -> 로딩 과정에서 java.lang.String, System Class를 읽는다 -> 클래스를 읽어오는 과정에서 로드타임에 로딩한다

JIT 컴파일러(Just-In-Time compiler)

  • 바이트 코드에서 반복되는 코드 부분은 JIT 컴파일러가 미리 네이티브 코드로 변환 시켜 놓는다
  • 반복되는 코드가 읽힐 때 인터프리터로 읽지 않고 바로 네이티브 코드로 변환 시켜 놓는다
  • 인터프리터의 단점인 속도 효율성을 보완하기 위해 도입이 되었다
  • 프로그램이 실행 중인 런타임에 실제 기계어로 변환해주는 컴파일러를 의미한다
  • 동적 번역(dynamic translation)이라고 불리는 이 기법은 프로그램의 실행 속도를 향상시키기 위해 개발되었다
    • 동적 번역 - 일부만 컴파일
  • 자바 컴파일러가 생성한 자바 바이트 코드를 런타임에 바로 기계어로 변환하는데 사용한다

가비지 컬렉터(garbage collector)

  • 더이상 사용하지 않는 메모리를 자동으로 회수한다
  • 필요에 따라서 성능 효율을 높이기 위해 커스터 마이징을 하여 사용이 가능하다

Runtime Data Area

  • 메모리 영역을 목적에 따라 5개로 나눌 수 있다.
  • Method Area
    • Class 정보, 변수 정보, Method 정보, static 변수 정보, final 정보등 저장 되는 영역이다
    • 모든 쓰레드에게 공유
  • Heap Area
    • new 명령어로 생성된 인스턴스와 객체들이 저장되는 구역이고 공간이 부족해지면 GC(Garbage Collection)이 실행되어 메모리 공간을 정리해준다.
    • 모든 쓰레드에 공유
  • Stack Area
    • Method 안에서 사용되는 값들인 매개변수, 지역변수, 리턴값 등이 저장되는 구역 메소드가 호출될 때 push되고 메소드가 실행되어 완료되면 pop으로 지워진다
    • 각 쓰레드마다 하나씩 생성
  • JVM Stack
    • Stack Frame을 저장하는 스택이다. JVM은 Stack Frame을 push하고 pop하는 작업을 한다.
  • PC Register
    • 각 쓰레드마다 하나씩 생성되고 현재 수행 중인 JVM 명령의 주소값이 저장된다.
  • Native Method Stack
    • Java 언에 이외의 다른언어의(C, C++) 메소드 호출을 위해 할당되는 언어에 맞게 Stack이 형성되는 구역이다.
    • 각 쓰레드마다 하나씩 생성

컴파일 하는 방법

  • 컴파일이란

    • 고급 프로그래밍 언어에서 쓰여진 프로그램으로 소스코드에서 오브젝트로 변환 되는 것이다
  • 컴파일러, 인터프리터

    • 고레벨 언어를 저레벨언어로 변경하기 위해 필요한 장치 또는 도구이다
  • 컴파일러는 전체소스코드를 보고 명령어를 수집하고 재구성

    • 컴파일러는 고레벨언어를 바로 기계어로 변환한다.
  • 인터프리터는 소스코드의 각 행을 연속적으로 분석하며 실행한다.

    • 고레벨 언어를 바로 기계어로 변는 것이 아닌 중간형태로 변환 한다음 실행한다.

인터프리터의 특성 4가지

  1. 컴파일러는 소스코드 전체를 한 번 훑고 컴퓨터 프로세서가 실행 할 수 있도록 바로 기계어로 변환한다.
    인터프리터는 고레벨 언어를 중간 코드(intermediate code)로 변환하고 이를 각 행마다 실행한다. 이 중간 코드는 다른 프로그램에 의해 실행된다
  2. 일반 적으로 컴파일러가 각 행마다 실행하는 특성을 가진 인터프리터보다는 실행시간이 빠르다
  3. 컴파일러는 전체 소스코드를 변환한 뒤 에러를 보고하지만 인터프리터는 각 행마다 실행하는 도중 에러가 보고되면 이후 작성된 코드를 살펴보지 않는다. 이는 보안적인 관점에서 도움이 된다.
  4. 예를 들어 파이썬은 인터프리트 언어이고 C, C++는 컴파일러 언어이다. 자바는 컴파일러와 인터프리터 모두 사용한다

자바 컴파일러와 인터프리터의 차이

자바 컴파일러는 .java 파일을 javac(java compiler)가 바이트코드로 쓰여진 .class 파일로 변환한다.

컴파일러는 JVM을 위해 기계어로 변환을한다.

JDK의 컴파일 설명

파일명 역할 설명
javac.exe 컴파일러 자바소스코드를 바이트코드로 컴파일
java.exe 인터프리터 컴파일러가 생성한 바이트 코드를 해석하고 실행
javap.exe 역어셈블러 컴파일된 클래스파일을 원래의 소스로 변환
jar.exe 압축프로그램 클래스파일과 프로그램과 프로그램의 실행에 관련된 파일을 하나의 jar파일(.jar)로 압축/해제

실행하는 방법

cmd 창에서 간단하게 컴파일 과정을 확인하기 위해

text로 HelloWorld.java 을 만든다.

HelloWorld.java 자바소스파일 생성

public class HelloWorld {

    public static void main (String[] agrs) {
        System.out.println("Hello World");
    }

}

HelloWorld.java > HelloWorld.class (바이트코드로 컴파일)

C:\javaTest>javac HelloWorld.java
  • ! javac.exe: 자바소스코드를 바이트코드로 컴파일

자바소스파일인 HelloWorld.java을 자바 바이트코드로 컴파일 해서 HelloWorld.class 만들어짐

HelloWorld.class 파일 실행

jre에서 실행을 담당하는 파일로 아래와 같이 실행을 하면 우리가 작성한 자바소스코드 Hello World가 찍힌다.

C:\javaTest>java HelloWorld.java
Hello World

! java.exe: 컴파일러가 생성한 바이트코드를 해석하고 실행

컴파일된 HelloWorld.class 원래의 소스로 변환

C:\javaTest>javap HelloWorld.class

! javap.exe: 컴파일된 클래스파일을 원래의 소스로 변환

자바 바이트 코드(Java bytecode)란

JVM이 이해할 수 있는 언어로 변환된 자바 소스 코드를 의미이다.

자바 컴파일러에 의해 변환되는 코드의 명령어 크기가 1바이트라서 자바 바이트 코드라고 불리고 있다.

JDK와 JRE의 차이

JDK(자바 개발 도구 ,Java Development Kit)

  • JRE 의 상위 집합이고 JRE에 있는 모든 항목과 개발 툴이 추가된 것이다

JRE(자바 실행 환경, Java Runtime Environment)

  • JVM이 자바 프로그램을 동작 시킬 때 필요한 라이브러리이며 JVM 및 기타 구성 요소를 제공합니다.

'스터디 > LiveStudy' 카테고리의 다른 글

7주차 과제: 패키지  (0) 2020.12.30
6주차 과제: 상속  (0) 2020.12.21
5주차 과제: 클래스  (0) 2020.12.19
4주차 과제: 제어문  (0) 2020.12.09
2주차 자바 데이터 타입, 변수 그리고 배열  (0) 2020.11.20