자바로 소스 코드를 작성하고 실행하면 컴파일러가 실행되면서 컴파일이 진행됨.
-> 컴파일의 결과로 .java 확장자를 가졌던 자바 소스코드가 .class 확장자를 가진 바이트 코드로 파일이 변환됨
-> JVM은 운영 체제로부터 소스 코드 실행에 필요한 메모리를 할당받음 (그림 상의 런타임 데이터 영역(Runtime Data Area)
-> 클래스 로더(Class Loader)가 바이트 코드 파일을 JVM 내부로 불러들여 런타임 데이터 영역에 적재시킴. 자바 소스 코드를 메모리에 로드 시키는 것
-> 로드가 완료되면 실행 엔진(Execution Engine)이 런타임 데이터 영역에 적재된 바이트 코드를 실행시킴
1. 인터프리터(Interpreter)를 통해 코드를 한 줄씩 기계어로 번역하고 실행시키기
2. JIT Compiler(Just-In-Time Compiler)를 통해 바이트 코드 전체를 기계어로 번역하고 실행시키기
실행 엔진은 기본적으로 1번의 방법을 통해 바이트 코드를 실행시키다가, 특정 바이트 코드가 자주 실행되면 해당 바이트 코드를 JIT Compiler를 통해 실행시킴
Person person = new Person();
위의 예시에서 new Person()
이 실행되면 Heap 영역에 인스턴스가 생성되며, 인스턴스가 생성된 위치의 주소값을 person
에게 할당해주는데, 이 person
은 Stack 영역에 선언된 변수. 즉, 객체를 다룬다는 것은 Stack 영역에 저장되어 있는 참조 변수를 통해 Heap영역에 존재하는 객체를 다룬다는 의미. Heap 영역은 실제 객체의 값이 저장되는 공간.
Person person = new Person();
person.setName("김코딩");
person = null;
// 가비지 발생
person = new Person();
person.setName("박해커");
예시 첫째 줄에서 참조 변수 person
은 Person 클래스의 인스턴스의 주소값을 할당받고 이어서 "김코딩"이라는 문자열이 person
이 가리키는 인스턴스의 name
이라는 속성에 할당됨. 그런데 세번 째 줄에서 참조변수 person
에 null
이 할당됨으로써 기존에 person
이 가리키던 인스턴스와 참조변수 person
간의 연결이 끊어짐. 프로그램이 실행 중일 때 이처럼 아무도 인스턴스를 참조하고 있지 않다면 더 이상 메모리에 person
이 가리키던 인스턴가 존재해야할 이유가 없음. 가비지 컬렉터는 이렇게 아무한테도 참조되고 있지 않은 객체 및 변수들을 검색하여 메모리에서 점유를 해제하며, 그럼으로써 메모리 공간을 확보하여 효율적으로 메모리를 사용할 수 있게 해줌.
Young 영역에서는 새롭게 생성된 객체가 할당되는 곳이고 여기에는 많은 객체가 생성되었다 사라지는 것을 반복함. 이 영역에서 활동하는 가비지 컬렉터를 Minor GC라고 부름.
Old 영역에서는 Young영역에서 상태를 유지하고 살아남은 객체들이 복사되는 곳으로 보통 Young 영역보다 크게 할당되고 크기가 큰 만큼 가비지는 적게 발생함. 이 영역에서 활동하는 가비지 컬렉터를 Major GC라고 부름.
Young 영역과 Old 영역은 서로 다른 메모리 구조로 되어있기 때문에 세부적인 동작 방식은 다르지만 기본적으로 가비지 컬렉션이 실행될 때는 다음의 2단계를 따름.
1. Stop The World: 가비지 컬렉션을 실행시키기 위해 JVM이 애플리케이션의 실행을 멈추는 작업. 가비지 컬렉션이 실행될 때 가비지 컬렉션을 실행하는 스레드를 제외한 모든 스레드들의 작업은 중단되고, 가비지 정리가 완료되면 재개됨.
2. Mark and Sweep: Mark는 사용되는 메모리와 사용되지 않는 메모리를 식별하는 작업을 의미하며, Sweep은 Mark 단계에서 사용하지 않음으로 식별된 메모리를 해제하는 작업.
Stop The World를 통해 모든 작업이 중단되면, 가비지 컬렉션이 모든 변수와 객체를 탐색해서 각각 어떤 객체를 참고하고 있는지 확인. 이후 사용되고 있는 메모리를 식별(Mark)해서 사용되지 않는 메모리는 제거(Sweep)하는 과정을 진행함.