- 변수에 메모리가 어떻게 저장되고 사용되는지, 메서드가 어떻게 호출되고 메모리에 어떤 변화를 일으키는지 살펴보았다
JDK, JRE, JVM 관계
- JDK : Java Development Kit, 자바 개발 환경 - 컴파일러, 역어셈블러, 디버거, 의존관계분석 등 개발에 필요한 도구 제공
- JRE : Java Runtime Environment, 자바 실행 환경 - 자바 실행 명령, 클래스로더와 바이트코드의 실행에 필요한 기본 라이브러리 제공
- JVM : Java Virtual Machine, 자바 가상 머신 - 바이트코드 인터프리터, JIT 컴파일러, 링커, 명령어 세트, 가비지 컬렉터, 런타임 데이터 영역(메모리) 등 OS에 독립적으로 실행될 수 있는 추상층 제공
- 즉, JDK를 이용해서 바이트 코드를 만들고 JRE를 사용해서 바이트 코드를 실행하면 JVM이 기동되면서 바이트 코드의 실질적인 실행을 담당한다
- JVM 스펙(https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-5.html#jvms-5.2)
JVM
- JVM(Java Virtual Machine)
- 자바 가상 머신의 약자로 자바 프로그램 런타임 환경을 제공하는 소프트웨어
- Java와 OS 사이에서 중개자 역할을 수행하여 Java가 OS에 구애받지 않고 실행되도록 한다
- 프로그램 메모리 관리도 하며 최적화도 수행한다
Java 프로그램의 수행 순서
- Java 프로그램이 실행되면 JVM은 OS로부터 해당 프로그램이 필요로 하는 메모리를 할당받는다.
- JVM은 할당받은 메모리를 용도에 따라 여러 영역으로 나누어 관리한다
- 자바 컴파일러(javac)가 자바 소스 코드를 읽어들여 자바 바이트 코드(.class)로 변환시킨다
- 컴파일 세부단계
- Lexical Analysis(어휘 분석)
- Syntax Analysis(구문 분석)
- Symantic Analysis(의미 분석)
- Intermediate Code Ceneration(중간 코드 생성) = 바이트 코드 생성
- Code Optimization(중간 코드 최적화)
- 클래스 로더를 통해 자바 바이트 코드를 JVM Runtime Data Areas(메서드 혹은 스태틱 영역)로 로딩한다
- 로딩된 바이트 코드들은 Execution Engine을 통해 해석된다
- interpreter : 실행 중 프로그래밍 언어를 한줄씩 읽어가며 기계어 코드로 번역 후 실행
- JIT Compiler : 실행 시점에서 인터프리터 방식으로 기계어 코드를 생성하면서 그 코드를 캐싱하여 같은 함수가 여러번 불릴 때 매번 기계어 코드를 생성하는 것을 방지
- 해석된 바이트 코드는 JVM Runtime Data Areas에 배치되어 실질적인 수행이 이루어진다
- 실행 과정에서 JVM은 필요에 따라 Garbage Collection과 같은 메모리 관리 작업을 수행한다
Runtime Data Areas
- JVM 단위에 속하는 힙과 메서드 영역은 JVM이 시작될 때 생성되고 JVM이 종료될 때 소멸되며 JVM 하나에 힙 하나, 메서드 영역도 하나가 생성된다
- 힙 : 인스턴스와 배열이 동적으로 생성되며 가비지 콜렉터의 대상이 된다. 동기화 문제 발생 가능성 있음
- 메서드 영역 : 클래스 로더가 적재한 클래스(또는 인터페이스)에 대한 메타데이터 정보가 저장되며 이 영역에 등록된 클래스만이 힙에 생성될 수 있다
- 스레드 단위에 속하는 PC 레지스터, JVM 스택, 네이티브 메소드 스택은 스레드 별로 생성된다
- PC 레지스터 : 현재 수행중인 JVM Instruction의 주소를 가짐
- JVM 스택 : 스레드의 메서드가 호출될 때 수행 정보(멤소드 호출 주소, 매개 변수, 지역 변수, 연산 스택)가 Frame이라는 단위로 JVM 스택에 저장되며 메서드 호출이 종료될 때 스택에서 제거된다
- 네이티브 메소드 스택 : java 외의 언어로 작성된 네이티브 코드들을 위한 스택
- 힙과 메서드 영역은 모든 스레드가 공유하게 된다
멀티 프로세스 / 멀티 스레드
- 멀티 프로세스는 다수의 데이터 저장 영역을 가진다
- 멀티 스레드는 스택 영역을 스레드 개수만큼 분할해서 사용한다
- 하나의 스레드에서 다른 스레드의 메서드 영역과 힙 영역을 공유해서 사용한다