article thumbnail image
Published 2021. 10. 11. 14:49
반응형

JVM이란 무엇인가?

JVM(Java Virture Machine)


  • 자바 가상 머신으로 자바 바이트 코드(.class 파일)를 실행할 수 있는 공간이다.
  • 가상 머신이므로 어떤 OS에서도 독립적으로 실행이 가능하다.
  • 핵심은 JVM은 OS 위에서 동작하게 되며, 자바 코드를 컴파일한 바이트 코드를 운영 체제가 이해할 수 있는
    기계어로 변환하여 코드를 실행 시킨다.

JVM 구성요소


1. ClassLoader

  • 바이트 코드를 JVM이 운영 체제로 부터 할당받은 메모리 영역인 Runtime Data Area로 적재하는 역할
  • 메모리를 할당 받았기 때문에 당연히 애플리케이션을 실행하면 수행된다.
  • .java 파일을 javac.exe가 컴파일한 .class(바이트 코드)가 적재된다.
    적재되는 시점은 Runtime시 적재된다.
  • 이름을 보면 이해할 수 있다. Class를 불러온다는 의미 그대로 생각하면 좋다.

2. Execution Engine

  • Runtime Data Area(메모리 영역)에 적재된 바이트 코드를 기계어로 변경하여 실행한다.
  • 기계어로 변경 후 명령어 단위로 실행하게 되는데 JIT컴파일러와 Interpreter 방식이 있다.

3. Garbage Collector

  • Heap 메모리 영역에 적재된 객체들 중 참조되지 않는 객체들을 제거한다.
  • GC가 언제 실행되는지는 알 수 없다.
  • JVM에서 우리가 가장 잘 알아야하는 부분이다. 다른 부분은 그렇구나 정도만 알면 된다.
    GC는 실제로 성능에 관련되어서 커스터 마이징 할 때가 있다고 한다.

4. Runtime Data Area(자바 런타임 메모리)

  • 메모리 영역으로 자바 애플리케이션이 실행할 때 사용되는 데이터가 적재되는 곳이다.
  • 총 5가지 영역이 존재한다.

Method Area

  • 클래스 수준의 정보(클래스 이름, 부모 클래스 이름, 메서드, 변수)를 저장한다.
  • static String name = "dugi" String name1 = "dugi1"
  • 애플리케이션에서 공유되는 영역이다. 즉, 모든 쓰레드가 공유
  • 이 부분이 이해가 조금 안됐는데 메타 데이터가 들어있다고 생각하면 된다.

Heap Area

  • new 키워드로 생성된 객체와 배열이 생성되는 영역
  • Garbage Collector에 의해 사라질 때 까지 남아있다.
  • 쓰레드가 공통으로 사용하는 영역이다.

Stack Area

  • 지역 변수, 파라미터, 리턴 값이 저장된다.
  • Primitive 타입은 값도 저장된다. 하지만 참조 타입은 참조의 주소를 자장한다.
  • 쓰레드 마다 개별 생성되는 영역
  • 즉, main 쓰레드를 추가로 생성하지 않으면, 한 개만 존재한다.

PC(Program Counter) Register

  • 쓰레드가 생성될 때 마다 생성되는 영역
  • 현재 쓰레드가 실행되는 주소와 명령을 저장하고 있기 때문에 이를 이용해 쓰레드를 돌아가면서 실행할 수 있다.

Native Method Stack

  • 자바 외 언어로 작성된 네이티브 코드를 위한 메모리 영역이다.
  • 보통 c의 코드를 수행하기 위한 영역이다.
  • Object 클래스의 hashCode 메서드가 native 타입이다. 실제 OS의 메서드이다.
  • Native 메서드를 사용하기 위해서는 반드시 JNI(네이티브 메서드 인터페이스)를 사용해야 하는데
    hashCode 메서드가 JNI가 되는 것이고 JNI가 네이티브 메서드 라이브러리를 호출하여 사용하게 된다.

Class Loader의 구성요소

  • 클래스 로더는 로딩, 링크, 초기화로 나뉜다.
    • 클래스 로더가 .class 파일을 읽어 기계어를 만들고 메서드 영역에 저장한다.
      이 때 저장하는 데이터는 FQCN(풀패키지경로 + 클래스명), 클래스/인터페이스/이넘, 메서드와 변수이다.
    • 로딩이 끝나면 Class 객체를 생성하여 힙 영역에 저장한다.
      Class 객체란 Class class = Solution.class인 타입을 뜻한다.
    • 클래스 로더는 세가지 종류를 가지는데 Bootstrap -> Extension -> Application이다.
      가장 먼저 최상위 부모인 Bootstrap에게 .class 파일을 로딩하라고 요청하고 없으면 Application까지 내려온다.
      만약 없으면 ClassNotFoundException을 만나게 되는 것이다. Class 객체에는 많은 정보들이 들어있다.
      아래와 같이 보면 알 수 있다. 마지막 최상위인 bootstrap 클래스 로더는 native이기 때문에 볼 수가 없다고 한다.링크
    • @Test void ddd() throws NoSuchFieldException {
          Solution solution = new Solution();
          Class<Solution> aClass = (Class<Solution>)
                  solution.getClass(); // 클래스 로더 정보
      
          System.out.println("==========클래스로더 정보==========");
          System.out.println("classloader = " + aClass.getClassLoader());
          System.out.println("parent classloader = " + aClass.getClassLoader().getParent());
          System.out.println("parent parent classloader = " + aClass.getClassLoader().getParent().getParent()); // 클래스 정보들 
          System.out.println("==========클래스 정보==========");
          System.out.println("class name = " + aClass.getName());
          System.out.println("field a = " + aClass.getDeclaredField("a"));
          System.out.println("field str = " + aClass.getDeclaredField("str")); 
          /* 결과 
          ==========클래스로더 정보========== 
          classloader = sun.misc.Launcher$AppClassLoader@18b4aac2 
          parent classloader = sun.misc.Launcher$ExtClassLoader@545997b1 
          parent parent classloader = null 
          ==========클래스 정보========== 
          class name = Solution 
          field a = private int Solution.a 
          field str = private java.lang.String Solution.str         
           */
      }
    • Verify : .class 파일이 잘 만들어졌는지 검증한다. 혹시 .class 파일을 내가 막 조작한다면 여기서 걸리는 것이다.
    • Preparation : 클래스 변수(static)와 기본값에 필요한 메모리를 준비하는 과정
    • Resolve : 심볼릭 메모리 레퍼런스를 메서드 영역(힙 영역아닌가?)에 있는 실제 레퍼런스로 교체하는 과정.
      // 실제 힙에 있는 레퍼런스가 아니라 논리적인 레퍼런스이다.
      Solution solution = new Solution(); // 심볼릭 레퍼런스
      초기화
    • optional이라 항상 그런건 아니다.
    • Preparation에서 준비한 메모리에 static한 변수를 할당한다.
  • 로딩

컴파일 하는 방법 및 실행하는 방법


컴파일 이란?

  • 사람이 해석하기 쉬운 고급 언어를 컴퓨터가 이해하는 바이너리 코드(기계어)로 변환하는 것.

컴파일 하는 방법

  • javac.exe 컴파일러를 통해 바이트 코드(.class)로 변환
  • 명령어 : javac User.java
  • 컴파일이 완료되면 컴파일을 실행한 위치에 .class 파일이 생성된다.

실행 방법

  • 컴파일된 .class 파일을 실행한다.
  • 명령어 : java User -> .class는 제외해야 함.

Javac.exe, java.exe가 어떤 위치에서도 실행될 수 있는데 이유는 환경변수가 해당 경로를 보고 있기 때문이다.

바이트 코드란?


먼저 바이너리 코드의 이해가 필요하다.

바이너리 코드

  • C언어 컴파일러는 소스 파일을 컴파일 하면, 바로 바이너리 코드 가 된다.
    이진 코드(0 ,1)이지만 기계어는 아닌 상태이므로 운영체제가 실행이 불가하다.
    기계어 는 운영체제가 유일하게 실행할 수 있는 바이너리 코드 이며, CPU 마다 다를 수 있다.
    CPU 마다 다를 수 있다는 것은 단지 1과 0의 조합이 다를 수 있다는 의미일 뿐이다.

바이트 코드

  • 자바는 바이너리 코드 과정을 거치지 않고 JVM이 바로 바이트 코드로 변환한다.
  • JVM이 이해할 수 있는 코드이다.
  • 바이트 코드는 다시 인터프리터 or JIT컴파일러 를 통해 바이너리 코드로 변환하게 된다.

JIT 컴파일러란


인터프리터와 컴파일러란?

  • 인터프리터
    실행되는 코드를 한 줄씩 해석한다.
    같은 코드가 다시 나와도 계속 반복하여 해석한다.
    즉, 초기 로딩이 빠르다. 단 계속 같은 코드도 다시 해석 하므로 느릴 수 있다.
  • 컴파일러
    딱 한 번 코드를 컴파일 하기 때문에 초기 로딩은 느리다.
    이후에는 다시 해석하지 않고, 캐싱하므로 더욱 빠를 수 있다.

JIT(Just In Time) 컴파일러

  • 인터프리터가 한 줄씩 해석하면서 기계어로 변환하는데, 반복되는 코드는 JIT 컴파일러가 한방에 기계어로 변환하여,
    캐싱해두고 재사용을 하게 된다.
  • 즉, 필요한 것만 컴파일 하는 것이다.
  • 인터프리터의 단점을 보완한 것이다.
  • 컴파일러는 한방에 기계어로 변환하기 때문에 초기에 느린 단점이 있다.
  • 이런 장단점으로 인해 JVM이 인터프리터 방식과 JIT 방식을 섞어 쓰는 것이다.

javac와 JIT 컴파일러

javac도 컴파일러 JIT도 컴파일러? 도대체 무슨 말이지?

  • javac는 JVM외부에서 자바소스를 JVM이 해석할 수 있는 언어인 바이트 코드로 변환하는 컴파일러입니다.
    컴파일러라는 것이 OS가 해석할 수 있는 기계어로 변환하는 것인데 JVM도 가상 OS입니다.
    그렇기 때문에 JVM OS의 언어인 바이트 코드로 변환하는 것입니다.
  • JIT 컴파일러와 인터프리터는 JVM의 컴파일러입니다. 이제 JVM이 아닌 실제 OS가 해석할 수 있도록 기계어로 변환하는 것입니다.
    즉, JVM 내부에서 실제 windows, mac, linux와 같은 OS가 해석할 수 있도록 해당 기계어로 변환하는 컴파일러 입니다.

JDK와 JRE 차이


JRE(Java Runtime Environment)

  • 자바 실행 환경으로 실행만 가능하다.(java.exe)
  • 즉, 소스코드 작성 불가.
  • 실행만 하는 환경이라면 JRE만 있으면 된다.

JDK(Java Development Kit)

  • JRE를 포함한 패키지로 소스코드도 작성할 수 있다.
  • 즉, 실행도 가능하고, 개발도 가능한것으로 실행만 할 것이라면 JDK는 필요없다.

Java의 세 가지 Edition

  • Java SE(Java Standard Edition)
    가장 기본 버전으로 우리가 하는 대부분 웹 개발 모두 가능하다.
  • Java EE(Java Enterprise Edition)
    웹 서비스, 메일 송신 등 기업에서 필요로 하는 기능들이 포함 되어있어 더욱 편리하게 사용할 수 있다. 하지만 SE로 보통 가능하다.
  • Java ME(Java Micro Edition)
    휴대전화, 가전제품과 같은 임베디드 지원 SE에 비해 기능은 적지만, 리소스가 적게 들어 임베디드에 적합.
반응형
복사했습니다!