자바 클래스 파일 (.java)
Java 를 가지고 작성한 자바 소스 코드(.java)를 JVM 이 이해할 수 있는 자바 바이트 코드(.class)로 변환
Java 를 설치하면, javac.exe 라는 실행 파일 형태로 설치된다.
자바 컴파일러
"Compiler" = "컴퓨터의 명령어 번역 프로그램"
'코드 번역기'
Java Virtual Machine, 자바 가상 머신
'자바 프로그램 실행 환경'을 만들어 주는 소프트웨어
자바 실행 환경(JRE, Java Runtime Environment)에 포함되어 있음
→ 현재 사용하는 컴퓨터의 운영 체제에 맞는 JRE 가 설치되어 있다면, JVM 이 설치되어 있다는 뜻
자바 코드를 컴파일하여 .class 바이트 코드로 만들면, 이 코드가 자바 가상 머신 환경에서 실행됨
Java 언어로 작성된 Test.java 를 컴파일하면, Test.class 파일이 생성됨
→ JVM 은 이 바이트 코드를 각각의 플랫폼에 설치되어 있는 운영 체제에 맞는 실행 파일로 바꿔준다.
하나의 바이트 코드(.class)로, JVM이 설치되어 있는 모든 플랫폼에서 동작 가능
→ 각각의 플랫폼에 맞게끔 컴파일을 따로따로 해줘야 할 필요 X
→ 플랫폼에 영향을 받지 X
→ JVM 을 사용하는 Java 어플리케이션은 JVM 하고만 상호작용을 하므로, OS가 달라지더라도 프로그램의 변경 없이 실행이 가능
실행 엔진(Execution Engine)은 바이트 코드를 JVM 내부에서 기계가 실행할 수 있는 형태로 변경하는데
이때 인터프리터, JIT 컴파일러를 사용한다.
실행 엔진 (Execution Engine)
- 클래스 로더를 통해, 런타임 데이터 영역에 배치된 바이트 코드들를 명령어 단위로 읽어서 실행
Java bytecode
→ 자바 컴파일러에 의해 변환되는 코드의 명령어 크기가 '1바이트'이므로, 자바 바이트 코드라고 불린다.
운영체제가 읽을 수 있도록 Java 컴파일러가 변환한 코드
자바 컴파일러에 의해, JVM 이 이해할 수 있는 언어로 변환된 자바 소스 코드
JVM 만 설치되어 있으면, 어떤 운영 체제에서라도 실행 가능
자바 컴파일러를 통해, 자바 소스 코드(.java)가 전달
이 코드를 Bytecode 로 인코딩한다.
소스 코드를 Bytecode 로 변환하는 동안 컴파일러는 다음 단계를 따른다.
1) Parse : .java 소스 파일을 읽은 후, 결과 토큰을 AST(Abastract Syntax Tree) 노드에 매핑
2) Enter : 정의된 심볼들을 심볼테이블(Symbol table) 에 저장
3) Process annotations : 요청된 경우, 지정된 컴파일 위치에서 찾은 애너테이션을 처리
4) Attribute : 구문 트리에 속성 부여 (이름 확인, 유형 검사 및 상수 정의)
5) Flow : 이전 단계의 트리에 대한 데이터 흐름을 분석 (할당 및 도달 가능성에 대한 검사)
6) Desugar : AST를 다시 작성하고, 몇몇 syntactic sugar들을 번역
7) Generate : .class 파일을 생성
Terminal에서 컴파일
1) 작성한 코드가 있는 위치로 이동해 javac 파일명.java 명령어를 작성
2) .class파일 생성 확인
운영체제가 읽은 바이트 코드 -> 기계가 실행 가능한 기계어로 번역
바이트 코드 명령어를 하나씩 읽어서, 네이티브 코드로 변환하고 실행
해석은 빠르나, 실행 시간이 길고 느리다
Just-In-Time 컴파일러
인터프리터의 단점(느린 실행 시간)을 보완하기 위해 도입
→ 인터프리터 방식으로 실행하다가, 적절한 시점에 바이트 코드 전체를 네이티브 코드로 바꾼다.
→ 그 다음부터 인터프리터는 네이티브 코드로 컴파일된 코드를 바로 사용한다.
→ 즉, 자바 인터프리터의 효율을 높여주는 것!
장점
메소드의 수행 빈도 多 경우, 인터프리터 로 컴파일 하는 것이 유리 (한 번만 실행되는 코드 등...)
메소드의 수행 빈도 小 경우, JIT 컴파일러로 컴파일 하는 것이 유리
바이트 코드를 IR(Intermediate Representation, 중간 단계의 표현)로 변환하여 최적화를 수행하고
그 다음 네이티브 코드를 생성
Intermediate Representation
- 소스 코드를 표현하기 위해 컴파일러 or 가상 시스템에서 내부적으로 사용하는 데이터 구조 or 코드
- 최적화, 번역 등... 추가 처리를 위해 도움이 되도록 설계되었다.
Java 데이터를 저장하는 영역
운영체제로부터 JVM이 할당받은 메모리 영역
Java.class 바이트 코드 -> 메모리 영역에 담는 운반기
런타임 中 자바 클래스들을 동적으로 JVM 에 로딩하는 역할
→ 실행 中 필요한 클래스 파일을 메모리에 올리는 역할
→ Java 클래스는 한번에 메모리에 올라가지 않고, 클래스 로더에 의해 어플리케이션에서 필요 할 때만 메모리에 올라가게 된다.
클래스 로더 덕분에,
JVM 은 Java 프로그램 실행 中 어떤 파일이 있어야 하는지 알지 않아도 된다.
계층 구조
클래스 로더끼리 부모-자식 관계를 이뤄 계층 구조로 생성된다.
→ 최상위 클래스 로더는 부트스트랩 클래스 로더(Bootstrap Class Loader)
위임 모델
계층 구조를 바탕으로, 클래스 로더끼리 로드를 위임하는 구조로 동작한다.
클래스를 로드할 때, 먼저 상위 클래스 로더를 확인하여
가시성(visiblility) 제한
하위 클래스 로더는 상위 클래스 로더를 찾을 수 O,
상위 클래스 로더는 하위 클래스로더의 클래스를 찾을 수 X
언로드(unload) 불가
클래스 로더는 클래스를 로드할 수 O, 언로드할 수는 X
그 대신, 현재 클래스 로더를 새로 삭제하고 아예 새로운 클래스 로더를 생성하는 방법을 사용할 수 있다.
Heap 영역에 참조되지 않은 객체를 제거 하는 역할
더는 사용하지 않는 메모리를 자동으로 회수
개발자가 따로 메모리를 관리하지 않아도 되므로, 더욱 손쉽게 프로그래밍을 할 수 있도록 도와줍니다.
JVM 으로 자바 바이트 코드(.class 파일)를 OS에 특화된 코드로 변환하여 실행한다.
그 이유는?
일반 어플리케이션의 코드
Java 어플리케이션
Java Runtime Environment, 자바 실행 환경
JVM 을 설치하기 위해서는 JRE를 설치해야 한다.
Java Development Kit, 자바 개발 키트
컴파일러 역할
JRE(JVM)의 기능을 포함하고 있다.
디버깅하는 jdb 등...의 기능이 있다.
JVM 은 가장 먼저 해당 클래스의 main 메소드를 실행시킨다.
public class Main {
public static void main(String[] args) {
System.out.println("Hello world!");
}
}
main 클래스의 이름은 .java 의 이름과 동일해야 한다.
참고: 자바 프로그래밍
참고: Java ClassLoader 알아보기
참고: [1주차] JVM은 무엇이며 자바 코드는 어떻게 실행하는 것인가.
참고: Java(자바) 코드를 실행하면 벌어지는 일
참고: [Java] 자바코드가 실행되는 과정에 대해 알아보자