- 자바 소스코드부터 JVM 구동까지의 전반적인 과정
- Runtime Memory, Class Loader, Executor Engine 등의 JVM 내부 구조
객체지향이란, 프로그램을 객체의 상호작용으로 보는 패러다임을 말한다. '객체' 란 현실 세계의 물체를 닮아있으며, 객체 지향에서 객체는 상태값과 메소드를 가진다. 상태값은 말 그대로 특정 객체 인스턴스가 가지는 상태값이며, 메소드는 해당 객체가 할 수 있는 동작, 일을 정의한다.
객체 지향 프로그래밍에서는 이러한 객체를 정의하고, 객체의 상태값과 메소드를 이용해 일련의 기능을 수행하도록 한다. 자바는 객체를 class로 표현한다. 자바의 모든 소스코드는 이 클래스 안에 존재한다.
개발자가 작성한 .java 소스코드 파일은 javac 컴파일러에 의해 .class 확장자를 가진 자바 바이트코드로 변환된다. 바이트코드의 타겟은 자바 바이트코드를 실행하는 기계 JVM이다. JRE(JVM + 라이브러리 파일들)가 갖춰졌다면 하나의 자바 바이트코드는 어디서든지 실행될 수 있다. 이는 OS나 아키텍처에 따라 다른 컴파일러를 사용해야하는 다른 컴파일 언어와 비교된다.
"new" 키워드를 통해 프로세스의 동적 메모리 공간인 힙에 객체 인스턴스를 생성할 수 있다. 프로그램이 동작하다보면 더이상 사용되지 않는 인스턴스가 발생하기 마련인데, 이때 인스턴스의 삭제를 개발자가 직접 하지 않고 자바의 Garbage Collector가 대신 진행한다. C계열 언어의 경우, 개발자가 malloc()을 통해 할당한 메모리는 직접 free() 시켜줘야한다.
개발자가 자바 소스코드를 작성한 뒤 실제로 자바 프로그램이 구동되기까지 어떤 과정이 일어날까?
.java
확장자의 소스파일을 작성한다..class
확장자를 가진 byte code 파일이 만들어진다. 이는 JVM이 직접 실행할 수 있는 코드이다..class
파일의 클래스들을 JVM의 메모리에 동적으로 로드한다.1~3은 컴파일 타임, 4~5는 런타임에 일어나는 일이다. 이제 JVM의 각각의 요소에 대해 더 자세히 살펴보자!
클래스 로더는 자바 프로그램에 필요한 클래스 정보를 메모리에 올리는, 말 그대로 클래스를 로드하는 "동적 로드"를 사용한다. 자바 프로그램이 동작하는데 필요한 클래스와 그 정보들을 프로그램의 시작 전 한꺼번에 올리는 것이 아니라, 처음 필요한 상황이 발생할 때 메모리에 올리는 것이다.
특정 클래스 타입을 이용하는 코드를 만났을 때, 클래스 로더는 파일 시스템에서 .class 파일을 찾아 다음의 과정을 거친다.
위 과정을 거쳐 초기화된 클래스는 JVM 런타임 메모리의 static 영역에 적재된다.
클래스 로더는 계층 구조를 가진다.
자바 프로세스가 시작하면 JVM은 OS에게 메모리를 할당받는다. 여기에 JVM이 사용하는 메모리 영역이 존재한다.
일반적인 컴파일링 언어는 소스코드가 컴파일러에 의해 object 파일로 변환되고, 이를 하드웨어가 직접 구동하는 방식을 따른다. 자바도 javac가 있는데 비슷한 거 아닌가? 싶을 수도 있는데, JVM은 실제 하드웨어가 아니라 "가상 머신"임을 상기해야한다. javac가 컴파일한 결과물은 JVM이 실행할 수 있는 파일이지, 실제 컴퓨터가 실행할 수 있는 파일은 아니다. 결국 실제로 명령어를 수행하는 것은 JVM이 설치된 하드웨어이다.
JVM이 실제 하드웨어를 이용해 자바 바이트코드를 실행하는 방식엔 크게 두 가지가 있다.
JIT(Just-In-Time) 컴파일러를 이용하면 실제 하드웨어가 이용하는 코드를 통해 기기를 동작시키고, 캐시를 이용할 수 있어 인터프리팅 방식보다 빠르게 프로그램을 돌릴 수 있다.
하지만 JIT의 컴파일링 시간 자체도 오래 걸리므로 내부적으로 바이트코드가 얼마나 자주 사용되는지 체크하여 일정 정도를 넘을 때만 컴파일링하는 것이 효율적이다. 한두 번만 사용되는 코드라면 인터프리팅 방식이 더 빠르기 때문이다.
https://d2.naver.com/helloworld/1230