그해
그래도해야지
그해
  • 그래도 해야지 (71)
    • Java (26)
    • Spring (8)
    • Golang (3)
    • CS (0)
      • 서버 (9)
      • 네트워크 (4)
      • 운영체제 (1)
      • WEB (0)
      • 데이터베이스 (6)
      • 자료구조 (1)
      • 보안 (3)
      • 알고리즘 (9)
    • 삽질 (0)
    • 회고 및 생각 (0)
hELLO · Designed By 정상우.
그해

그래도해야지

Java

JVM과 자바의 실행 과정

2023. 2. 16. 20:51

 

1.  JVM이란 무엇인가?

  • Java Vitual Macine 의 줄임말으로 자바를 실행하기 위한 가상 컴퓨터이다. 이때 가상 컴퓨터는 실제 컴퓨터가 아닌 소프트웨어로 구성된 컴퓨터라고 이해하면 된다. 자바로 작성된 모든 어플리케이션은 모두 이 가상 컴퓨터 즉, JVM으로 실행되기 때문에 자바를 실행하기 위해서는 무조건 이 JVM이 필요하다.

  • 일반 어플리케이션은 os만 거치고 하드웨어로 전달되는데, 자바 어플리케이션은 JVM을 한번더 거치기 때문에 실행 시 약간 속도가 느리다는 단점을 가진다. 하지만 다른 애플리케이션은 OS에 붙어있기때문에 OS에 종속적인 반면, 자바 어플리케이션은 OS와 하드웨어에 독립적이다. 단, JVM은 OS에 종속적이라 해당 OS마다 실행가능한 JVM이 다르다.

 

 

2. 자바 컴파일하는 방법/ 실행하는 방식

  1. 개발자가 자바 소스코드(.java) 를 작성한다.
  2. 자바 컴파일러(Javac)가 자바 소스파일(.java)을 컴파일 한다. 이때 나오는 파일(.class)은 자바 바이트 코드로 아직 컴퓨터가 읽을 수 없고, JVM만이 이해할 수 있는 소스코드이다.
  3. JVM의 클래스 로더를 통해 클래스들을 로딩해 JVM메모리에 올린다.
  4. 실행 엔진(Execution engine)은 JVM 메모리에 올라온 자바 바이트 코드들을 명령어 단위로 하나씩 가져와 실행한다. 이떄 실행 방식은 두가지인데
    1. 인터프리터 방식 : 자바 바이트 코드 명령어를 하나씩 읽어서 해석하고 실행한다. 실행은 빠르나 전체적인 실행 속도가 느리다는 단점을 가짐
    2. JIT (Just In Time) 컴파일러 : 위 인터프리터 단점을 보완하기 위해 도입된 방식으로 인터프리터 방식 + 컴파일 방식을 혼합한 방식이다. 자바 컴파일러(.javac)을 통해 변경된 자바 바이트 코드를 , 실행 시점에 인터프리터 방식으로 기계어 코드를 생성하면서 그 기계어 코드를 캐싱해 같은 자바 바이트 코드를 읽을때는 해당 메서드를 다시 읽지않고 이미 캐싱된 기계어 코드로 직접 실행하는 방식이다. 따라서 전체적인 실행속도는 인터프리팅 방식보다 빠르다. (뒤에 더 자세한 소개! ) 

 

4. 자바 바이트 코드란 무엇인가

  • JVM이 이해할 수 있는 언어로 변환된 자바 소스 코드. 자바 컴파일러에 의해 변환되는 코드의 명령어 크기가 1바이트라 자바 바이트 코드라 불린다. 이런 자바 바이트 코드의 확장자는 .class이다. 기계가 바로 수행할 수 있는 언어(ex , 0101010010) 보다는 비교적 인간이 보기 편한 형태로 기술되어있다.

 

 

5. JIT 컴파일러란 무엇이며 어떻게 동작할까?

  • JVM은 자바 바이트 코드를 만들때는 정적 컴파일러( Javac, 프로그램 실행전에 컴파일하는 방식)을 사용하지만, 바이트 코드를 기계어로 변환할 때는 JIT 컴파일러를 사용한다.
  • 이전에 자바는 인터프리터 방식을 사용했는데 이는 프로그램 실행중에 한줄씩 읽어내려가야하므로 속도가 느리다는 단점을 가지고 있었다. 이를 보완한 방법이 JIT 컴파일러다.
  • JIT 컴파일러는 인터프리터 방식으로 기계어로 변환된 코드를 캐시에 저장해 재사용시 컴파일을 다시 하지 않고 캐싱된 기계어를 사용하여 속도를 높였다.
  • 하지만 JVM의 캐시 공간은 매우 작기때문에 모든 코드들을 캐시하는 것은 아니다. JVM내부에서 자주 수행되는 코드를 선별해 캐시공간에 넣어준다. 이 JIT컴파일러 안에서는 C1 컴파일러와 C2 컴파일러가 존재하는데 c1은 런타임에 바이트 코드를 기계어로 변화하는 과정만 수행해며, C2는 런타임에 바이트 코드를 기계어로 변환해 캐시에 저장하는 과정을 수행한다 .

 

6.  JVM 구성요소

 

크게 Class Loader, Execution Engine, Runtime Data Area, GarbageCollector 로 나눌 수 있다.

자바 클래스 로더 (Class Loader)

: 자바 컴파일러(Javac)가 자바 파일을 컴파일하면 .class파일이 생성되는데 이렇게 생성된 모든 클래스 파일들을 엮어 RunTime Data Area 형태로 JVM 메모리에 적재하는 역할을 한다.

  • 로딩 절차
    1. 어떤 메소드를 호출하는 문장을 만났을 때, 그 메소드를 가진 클래스 바이트코드가 아직 로딩된 적 없다면, 곧바로 JVM은 JRE 라이브러리 폴더에서 클래스를 찾음.
    2. 없으면, CLASSPATH 환경변수에 지정된 폴더에서 클래스를 찾음.
    3. 찾으면 그 클래스 파일이 올바른지 바이트코드를 검증.
    4. 올바른 코드라면 Runtime Data Area의 Method Area로 파일을 로딩한다. → 이게 바로 클래스 로딩
    5. 클래스 변수를 만들라는 명령어가 있으면 Method Area에 그 변수를 준비한다.
    6. 클래스 블록이 있으면 순서대로 블록을 실행한다.
    7. 이렇게 한번 클래스의 바이트 코드가 로딩되면 JVM이 종료될때까지 유지된다.
  • 이 JVM의 Class Loader는 로딩 → 링킹 (Linking) → 초기화(Initialinzation) 기능을 동작한다.
    • 로딩 : 클래스 로더가 .class 파일을 읽어 적절한 바이너리 데이터를 만들고 Runtime Data Area의 Method Area에 데이터를 저장한다.
    • 링킹(Linking) : 레퍼런스? 연결하는 과정. 메모리와 메소드와 연결 어떤 참조변수가 힙에 저장된 실제 a 클래스의 주소를 연결.
      • verifty: 컴파일이 잘 되어있는지 확인하는 절차. 읽은 .class 파일 형식이 올바른지 여부를 확인한다.
      • prepare : 클래스의 static 변수 (정적 변수)와 기본값에 필요한 메모리를 준비한다.
      • resolve : 선택적으로 진행되는 과정으로 complile 단계에서 자바 클래스는 다른 클래스의 실제 주소 값을 알지 못하기때문에 이 단계에서 런타임 constant pool의 symbolic reference를 실제 메모리 주소값으로 변경해주는 작업을 한다.
    • 초기화(Initialization) : 링킹 단계의 prepare 단계에서 확보한 메모리 영역에 클래스의 static값들을 할당한다.

(static은 고정된이라는 의미로 static 변수와 static 메소드가 있는데 객체(인스턴스)가 아니라 클래스에 고정된 멤버이다. 그렇기 때문에 클래스 로더가 클래스를 로딩해서 메소드 메모리 영역에 적재할때 클래스 별로 관리한다. )

 RunTime Data Area

 

  • JVM이 프로그램을 수행하기 위해 os로 부터 할당받은 메모리 영역. 즉 자바 어플리케이션을 실행될때 사용되는 데이터를 저장하는 영역이며 5가지로 나누어져 있다.
  • 구성요소
    • Method Area (= 클래스 영역 = static 영역) : 모든 쓰레드가 공유하는 메모리 영역. 클래스 파일의 바이트 코드가 로드되는 곳! 프로그램 실행 중 클래스가 사용되면 JVM은 해당 클래스 파일을 읽어 분석해 클래스의 인스턴스 변수, 메서드 코드 등을 읽어 이곳에 저장한다. 클래스와 관련된 거의 모든 정보가 저장된다. 따라서 JVM구동시에 생성되어 종료때까지 유지된다.
    • Heap : 모든 쓰레드가 공유. 인스턴스화(new 연산자로 생성)된 모든 클래스 인스턴스와 배열, 객체를 저장한다. 참조하는 변수나 필드가 없다면 의미없는 객체가 되어 가비지 컬렉터의 대상이 된다.
    • Stack : 쓰레드 별로 1개씩 존재. 각자 쓰레드가 시작될때 생성된다. 메서드(클래스 안의 함수)가 수행될 때마다 하나의 스택프레임이 생성되어 해당 스레드의 JVM이 stack에 추가되고 메서드가 종료되면 스택 프레임이 제거된다.
    • Pc Registrer : 쓰레드 별로 1개씩 존재 . 쓰레드가 생성될 때마다 생기는 공간으로 쓰레드가 어떠한 명령을 실행하게 될지에 대한 부분을 기록한다.
    • Native Method Stack : 쓰레드 별로 1개씩 존재 . Java 외의 언어(ex c언어) 로 작성된 네이티브 코드들을 위한 stack.

 

 Execution Engine (실행 엔진)

  • class loader에 의해 메모리에 로드된 바이트 코드를 실행하는 곳 . 즉 class loader에 의해 runtime data area에 배치된 바이트 코드는 Execution Engine에 의해 실행된다.

 Garbage Collector (= GC)

  • 자바의 메모리 관리 방법 중 하나로 JVM의 heap 영역에서 동적으로 할당했던 메모리 영역 중에 필요 없게 된 메모리 영역을 주기적으로 삭제하는 프로세스.
  • ( C나 C++은 이러한 가비지 컬렉션이 없어 개발자가 수동으로 메모리 할당이나 해제를 일일히 해줘야한다. )

 

7.  JDK와 JRE의 차이 

 

  • JRE는 Java Runtime Environment의 약자로 자바로 만들어진 프로그램을 실행시키는 필요한 라이브러리 각종 API와 JVM이 포함되어 있다. 즉 JRE는 자바로 개발은 안되고 실행만 된다라고 이해하면 된다.
  • JDK는 Java Development kit의 약자로 개발자들이 자바로 개발하는데 사용된다. JDK안에는 개발 시 필요한 라이브러리들과 javac, javadoc이 포함되어 있고 개발을 하려면 당연히 실행도 시켜줘야 하기때문에 JRE도 포함되어 있다.

즉! JDK > JRE > JVM 으로 포함되어 있다. 

 

 

 

 

출처  및 참고 

- https://qiita.com/SHUAI/items/9cd4b78f874f5a11c48c

'Java' 카테고리의 다른 글

[JAVA] 다차원 배열  (0) 2023.12.06
[Java] String 배열  (0) 2023.12.06
[Java] 배열(Array)  (1) 2023.12.06
객체지향 설계 5원칙 - SOLID  (0) 2023.02.23
원시타입(Primitive type) vs 참조타입(Reference type)  (0) 2023.02.17
    'Java' 카테고리의 다른 글
    • [Java] String 배열
    • [Java] 배열(Array)
    • 객체지향 설계 5원칙 - SOLID
    • 원시타입(Primitive type) vs 참조타입(Reference type)
    그해
    그해
    그래도 공부는 해야지

    티스토리툴바