Java는 JVM에 의해 OS로부터 독립적이다.
하나의 Java 소스를 실행하기 위해서는 운영체제(OS)에 맞는 JRE(Java Runtime Environment, 자바 실행 환경)를 컴퓨터에 셋팅해주면 된다.
Java는 컴파일 과정을 통해 JVM(Java Vitual Machine)이 인식할 수 있는 파일을 생성하고 실행하여 실제 물리적 컴퓨터(real Machine) OS의 영향을 받지 않도록 한다.
Java source code(.java)
작성 후 Build
Java Compiler가 Java source(.java)
을 Compile하여 Java byte code(.class)
로 산출
이때, Java byte code(.class)
는 JVM이 이해할 수 있는 코드로, byte code의 각 명령어는 1byte 크기의 Opcode와 추가 피연산자로 이루어져 있다.
Compile된 Java byte code(.class)
를 클래스 로더(Class Loader)에 전달
클래스 로더는 동적로딩(Dynamic Loading)을 통해 필요한 클래스들을 로딩 및 링크하여 런타임 데이터 영역(Runtime data area), 즉 JVM의 메모리에 올린다.
동적 로딩을 통해 실행 시에 모든 클래스를 로딩하지 않고 필요한 클래스만 로딩하여 사용할 수 있으며, 일부 클래스가 변경되어도 전체 애플리케이션을 다시 컴파일하지 않아도 된다.
- 클래스 로더 세부 동작
1) 로드 : JVM이 런타임에 클래스를 요청할 때 클래스 로더는 클래스를 찾고 정규화된 이름을 사용해서 로드한다.(ClassLoader.loadClass() 메서드)
JVM은 Application ClassLoader(가장 아래에 있는 자식 클래스 로더)에게 최초 요청하고, 클래스가 아직 로드되지 않았다면 상위(부모) 클래스 로더에 책임을 위임한다. 만약 부모 클래스 로더에서도 클래스를 로드하지 못했다면 다시 자식 클래스 로더에게 다시 요청한다.
2) 검증 : 자바 언어 명세(Java Language Specification) 및 JVM 명세에 명시된 대로 구성되어 있는지 검증한다.
3) 준비 : 클래스가 필요로 하는 메모리를 할당한다.(클래스에서 정의된 필드, 메서드, 인터페이스를 나타내는 데이터 구조들 등)
4) 분석 : 클래스의 상수 풀 내 모든 심볼릭 레퍼런스를 다이렉트 레퍼런스로 변경한다.
5) 초기화 : 클래스 변수들을 적절한 값으로 초기화한다. (static 필드들을 설정된 값으로 초기화 등)
실행 엔진(Excution Engine)은 JVM 메모리에 올라온 byte code들을 Opcode단위로 하나씩 가져와서 실행.
- 실행 엔진의 실행 방식
⒜. 인터프리터(interpreter) : byte code 명령어를 하나씩 읽어 해석하고 실행. 개별 코드의 실행 속도는 빠르지만, 전체 실행 속도는 느리다는 단점이 있다.
⒝. JIT 컴파일러(Just-In-Time Compiler)
인터프리터의 단점을 보완하기 위해 도입된 방식. byte code 전체를 컴파일 하여 네이티브 코드로 직접 실행하는 방식. 처음 실행 속도는 느리지만, 캐시 사용으로 컴파일 이후 전체 실행 속도는 인터프리팅 방식보다 빠르다.