자바 프로그램 실행과정JVM 옵션 참조JVM 구성1. Class LoaderClassloadingCLASSPATH variableClass linkingClass Initialization2. Execution Engine (클래스를 실행시키는 역할) Class instantiationMethod executionApplication terminationInterpreterJIT(Just In Time)Garbage Collector3. Runtime Data Area(응용 프로그램 실행 시 사용하는 메모리의 구조)3-1) PC Register3-2) JVM 스택영역3-3) Native Method stack3-4) Method Area( = Class area = Static area)3-5) Heap 영역Minor GC : New 영역에서 일어나는 GCMajor GC(Full GC) : Old 영역에서 일어나는 GC
자바 프로그램 실행과정
- 프로그램 실행되면 JVM은 OS로부터 메모리 할당(JVM은 이 메모리를 용도에 따라 여러 영역으로 나누어 관리함)
- javac 이 .java → .class(자바 바이트 코드)로 변환함
- Class Loader를 통해 class 파일들을 JVM 으로 로딩
- 로딩된 class 파일들 Execution engine을 통해 해석(Interpreter or JIT)
- 해석된 바이트코드는 Runtime Data Areas 에 배치되어 수행이 이루어짐
- 위와 같은 실행과정 속에서 JVM은 필요에 따라 Thread Synchronization과 GC 같은 관리작업 수행함
JVM 옵션 참조
JVM 구성
[기계인간 ] JVM 메모리 구조와 GC

- JVM 의 구조는 2개의 Subsystem인
Class Loader
와Execution Engine
으로 구성됨 - 이들은
Runtime data memory area
(method area, heap, application thread stacks)를 이용하여 서비스 프로세스와 어플리케이션 스레드를 실행시킴
ClassLoader
는.class
파일을 읽어서 JVM메모리 상의 method area에 클래스 관련 데이터를 넣게 됨- 정적 필드, 메서드의 바이트코드, 클래스 메타데이터
Execution engine
은 아래 과정들을 통해 바이트 코드를 실행시킴- object 객체화(object instatiation)를 위하여 heap area를 사용
- Java 메서드와 native 메서드를 위한 stack ( 호출 과정을 추적하기 위한 용도)
- GC 프로세스
1. Class Loader
Class Loader 내부적으로 수행되는 프로세스들 (
Classloading
, Class linking
, Class initialization
)- JVM 내로 클래스(.class 파일)를 로드하고 링킹을 통해 배치하는 작업을 수행하는 모듈임
- jar 파일 내 저장된 클래스들을 JVM위에 탑재하고 사용하지 않는 클래스들은 메모리에서 삭제함
- 자바는 동적 코드, 컴파일 타임이 아니라 런타임에 참조함
- 로딩, 링크, 초기화 순서로 진행됨
- 클래스를 처음으로 참조할 때, 해당 클래스를 로드하고 링크함
- 자바는 완벽한 Dynamic Binding / Dynamic Linking 지원 Java 정적 바인딩과 동적 바인딩
- https://futurists.tistory.com/43 참고..
Classloading
- JVM 명세에 따르면 loading 단계에서는
.class
파일을 찾고(classpath에 리스트 되어있는 위치에서) 읽어서 내부 자료구조에 맞게 parsing 하여 method area에 넣고, java.lang.Class의 인스턴스를 만듦 - 제일 처음 로드되는 클래스는 커맨드 라인을 통해서 명시된 클래스를 부르게 됨(해당 클래스는
main(String[ ])
메서드를 갖고 있어야 함) - 클래스로더는 .class 파일을 읽고 parsing한뒤 정적 필드와 메서드 바이트 코드를 method area 에 채워 넣는다
- 그리고 그 클래스를 서술하는
java.lang.Class
인스턴스를 생성함 java.lang.Class
코드를 살펴보면 public 생성자가 존재하지 않는 것을 볼 수 있는데 클래스 로더가 인스턴스를 직접 생성하기 때문에 생성자가 없는 것- 이 인스턴스는 class에 대한 메타데이터 정보를 갖고 있음(class name, package, fields, constructors, method signature 등등)
- class의 static data(method area에 존재), state values(object 안에 존재. heap), method bytecode(method area에 존재)는 포함하지 않음
- 그 후, 클래스 로더는 클래스를 link(
class linking
)하고 초기화를 진행함(Class initialization
). 그리고 execution engine으로 바이트 코드 실행을 위해 넘겨줌
Class loader에 의해 만들어진 메모리 상에 존재하는 데이터는 execution engine에 의해 유지 관리되고
binary representation of the type
이라고 불림- main(String []) 정적 메서드는 어플리케이션의 진입점으로서, 여기서 다른 클래스의 메서드를 호출하게 되면 그 클래스는 classpath상에서 찾아서 load 되고, initialize 되어야 한다. 그래야만 해당 클래스의 메서드를 호출할 수 있기 때문임
모든 클래스는 main(String[] ) 를 가질 수 있다. 해당 메서드는 클래스를 독립적으로 실행시키거나 standalone 어플리케이션으로 실행하기 위해 사용된다.
해당 메서드가 있다고 해서 그 클래스가 자동으로 main 클래스가 되는 것이 아니고
java 커맨드와 함께 명시
가 되거나, jar 파일의 manifest
로 명시될 때 main 클래스가 됨CLASSPATH variable
- CLASSPATH 변수는 어디에서 user의 클래스들을 찾아야 하는지를 알려주는 변수임 (Classes that are part of the JRE, JDK platform, and extensions should be defined through other means, such as the bootstrap class path or the extensions directory)
- 확인하는 방법 :
String[] paths = System.
getProperty
("java.class.path").split(":");
- [참고] 환경변수 확인 :
System.getEnv()
Class linking
클래스 링킹 단계에서는 크게 3개의 태스크가 수행됨
- binary representation of a class or an interface의 검증
- JVM 은 .class file이 java compiler 에 의해 만들어졌을 거라고 기대하긴 하지만, 아닐수도 있기에 검증단계가 필요함
- 해당 단계에서 아래의 내용들을 진행
- method 호출의 argument가 method descriptor와 맞는지 확인
- method의 return type과 실제 return 명령어가 맞는지 등
- 또 다른 체크와 검증 프로세스가 있는데 JVM 벤더에 따라 조금씩 다름
- 정적 필드들을 method area에 저장(class 나 인터페이스의 정적 변수들이 method area에 만들어지고 그 타입의 디폴트 값으로 초기화됨
- symbolic reference를 concrete reference(method are를 가리키는)로 resolve
- 로드된 바이트코드가 다른 메서드나 인터페이스, 클래스를 호출하면 symbolic referenc가 concrete reference로 resolve가 됨
- 만약 참조되는 클래스나 인터페이스가 로드 되지 않았다면 클래스 로더가 찾아서 로드를 진행함
Class Initialization
- JVM 명세에 따르면, initialization은 class의 initialization method를 실행하며 수행됨. 이 과정은 프로그래머가 정의한 초기화(static block & static assignment)가 수행되면서 진행됨(다른 클래스에 의해 이미 초기화가 진행되지 않았다면)
- 다른 클래스에 의해 초기화가 진행되지 않았는지를 체크 하는 것은 중요함. 왜냐면 메서드는 여러 클래스에서 호출되기 때문에. JVM 프로세스는 여러 스레드에서 진행되고 동일한 클래스를 concurrent하게 접근하기 때문에 여러 스레드 사이의 coordination(also called synchronization)이 중요함
2. Execution Engine (클래스를 실행시키는 역할)
Execution Engine 안에서 실행되는 프로세스들 (
Class instantiation
, Method execution
, GC
, Application termination
)- 클래스를 실행시키는 역할
- 클래스 로더가 JVM내의 런타임 데이터 영역에 바이트 코드를 배치시킨 후, 이것이 실행 엔진에 의해 실행됨
- 자바 바이트 코드는 기게가 바로 수행할 수 있는 언어보다는 비교적 인간이 보기 편한 형태로 기술된 것임
- 실행 엔진은 이와 같은 바이트 코드를 실제로 JVM 내부에서 기계가 실행할 수 있는 형태로 변경함(Interpreter, JIT)
Class instantiation
- 이 스텝은 main method 안에서 new 연산자를 호출하지 않는다면 일어나지 않는 단계임
- 이 단계에는 아래와 같은 태스크가 수행됨
- heap 영역에 메모리를 할당함(오브젝트의 state를 위해)
- instance field를 디폴트 값으로 초기화함
- java 와 native 메서드의 thread stack을 생성( 각각의 어플리케이션 스레드는 각자의 런타임 stack이 생성됨)
Method execution
- 어플리케이션의 첫번째 스레드(main thread)는 main(String[]) 메서드가 실행되면서 생성됨
- 그리고 main thread가 다른 application thread들을 생성가능
- execution engine은 바이트코드를 읽고 이해해서 binary code를 microprocessor에게 실행을 위해서 보내게 됨
- 근데 이 엔진은 얼마나 해당 메서드들이 호출되는지를 카운트하고 있다가 특정 임계치 이상으로 자주 호출하게 되면 just-in-time(JIT) 컴파일러를 이용하여 해당 메서드의 바이트코드를 native code로 컴파일 해버림. 그래서 다음에 호출될 때는 interpretation이 필요하지 않게 만들어서 코드의 성능을 향상시킴
- 현재 실행되고 있는 instruction과 다음 instruction에 대한 주소는 program counter(PC) 레지스터에 유지되게 됨. 각각의 스레드는 각자의 PC register를 갖고 있고 그로 인해 성능 향상이 있음.
Application termination
어플리케이션의 종료는 여러 가지 방법이 있음
- 정상 종료, 에러 코드 없이
- 비정상 종료, unhandled exception
- 프로그래밍적 강제종료, 에러 코드 있거나, 없거나
스레드 타입에 따른 종료 상황
- 돌고 있는 스레드가 없다면 JVM 은 실행을 멈춤
- 메인스레드가 아닌 다른 스레드가 unhandled exception이 있어도, 다른 스레드들은 정상 작동함
- 메인 스레드가 unhandled exception을 던진다면 child thread들은 daemon이더라도 다 종료됨
- 돌고 있는 user의 child thread가 하나라도 있으면 JVM은 계속 동작함. 모든 유저 thread 가 종료될 때까지(daemon이라면 상관없음)
Interpreter
- 실행 엔진은 자바 바이트 코드를 명령어 단위로 읽어서 실행 → 한줄씩 수행하기에 느림
JIT(Just In Time)
- 인터프리터 방식의 단점을 보완하기 위한 컴파일러임.
- 인터프리터 방식으로 실행하다가 적절한 시점에 바이트코드 전체를 컴파일하여 네이티브 코드로 변경 후, 이후에는 더이상 인터프리팅 x
Garbage Collector
- gc를 수행하는 모듈(쓰레드)이 있음
3. Runtime Data Area(응용 프로그램 실행 시 사용하는 메모리의 구조)

3-1) PC Register
- 각 Thread가 시작될 때 생성되며 Thread 마다 하나씩 존재함
- Thread가 어떤 부분을 어떤 명령으로 실행해야 할 지에 대한 기록을 하는 부분으로, 현재 수행 중인 JVM 명령의 주소를 가짐
3-2) JVM 스택영역
- 스택 프레임(해당 메서드만을 위한 공간), 메서드 안에서 사용되는 값들 저장
3-3) Native Method stack
- 자바 프로그램이 컴파일되어 생성되는 바이트 코드가 아닌 실제 실행할 수 있는 기계어로 작성된 프로그램을 실행시키는 영역
- JAVA가 아닌 다른 언어로 작성된 코드를 위한 공간
- JAVA Native Interface를 통해 바이트 코드로 전환하여 저장하게 됨
3-4) Method Area( = Class area = Static area)
- 프로그램 실행 중 어떤 클래스가 사용되면, JVM은 해당 클래스의 클래스파일(*.class)을 읽어서 분석하여 클래스에 대한 정보(클래스 데이터)를 이곳에 저장함. 그 클래스의 클래스 변수도 이 영역에 함께 생성됨
- 클래스 정보를 처음 메모리 공간에 올릴 때 초기화되는 대상을 저장하기 위한 메모리 공간
- 올라가게 되는 메소드의 바이트 코드는 프로그램의 흐름을 구성하는 바이트 코드인데, 사실상 컴파일 된 바이트코드의 대부분이 메소드 바이트코드이기 때문에 거의 모든 바이트 코드가 올라간다고 봐도 상관없음
- 여기에 Runtime Constant Pool도 함께 존재함
- Field Information : 멤버변수의 이름, 데이터 타입, 접근 제어자에 대한 정보
- Method Information : 메소드의 이름, 리턴타입, 매개변수, 접근 제어자에 대한 정보
- Type Information : class 인지 interface인지의 여부 저장 / Type의 속성, 전체이름, super class의 전체 이름
올라가는 정보의 종류
3-5) Heap 영역

- 힙 영역이 5개로 나누어져 있는 이유는 효율적으로 GC가 일어나게 하기 위함임
Permanent Generation
- JDK 8부터는 permanent 영역 사라지고 일부가 meta space 영역으로 변경됨. meta space 영역은 Native stack 영역에 포함되었음
- 생성된 객체의 주소값이 저장된 공간
- Reflection을 사용하여 동적으로 클래스가 로딩되는 경우에 사용됨
- 내부적으로 Reflection 기능을 자주 사용하면 이 영역에 대한 고려가 필요함
New/Young 영역
- Eden : 객체들이 최초로 생성되는 공간
- Survivor 0 / 1 : Eden에서 참조되는 객체들이 저장되는 공간
Old 영역
- New area에서 일정시간 참조되고 있는 살아남은 객체들이
Minor GC : New 영역에서 일어나는 GC
1. 최초에 객체가 생성되면 Eden영역에 생성된다.
2. Eden영역에 객체가 가득 차게 되면 첫 번째 CG(minor GC)가 일어난다.
3. survivor0 영역에 Eden영역의 메모리를 그대로 복사된다. 그리고 survivor0 영역을 제외한 다른 영역의 객체를 제거한다.
4. Eden영역도 가득차고 survivor0영역도 가득차게된다면, Eden영역에 생성된 객체와 survivor0영역에 생성된 객체 중에 참조되고 있는 객체가 있는지 검사한다.
5. 참조 되고있지 않은 객체는 내버려두고 참조되고 있는 객체만 survivor1영역에 복사한다.
6. survivor1영역을 제외한 다른 영역의 객체들을 제거한다.
7. 위의 과정중에 일정 횟수 이상 참조되고 있는 객체들을 survivor1에서 Old영역으로 이동
- 위 과정을 계속 반복, survivor1영역까지 꽉 차기 전에 계속해서 Old로 비움
Major GC(Full GC) : Old 영역에서 일어나는 GC
1. Old 영역에 있는 모든 객체들을 검사하며 참조되고 있는지 확인한다.
2. 참조되지 않은 객체들을 모아 한 번에 제거한다.
- Minor GC보다 시간이 훨씬 많이 걸리고 실행중에 GC를 제외한 모든 쓰레드가 중지한다.
- Major GC가 일어날 때 Old 영역에서 참조가 없는 객체들을 제거함으로써, Heap 메모리 영역 중간중간에 구멍이 생기게 되는데 이 부분 없애기 위해 재구성을 하게 됨 → 메모리를 정리하고 있을 때 다른 쓰레드가 메모리를 사용할 수 없기에 모든 쓰레드가 정지됨