JVM
작성한 코드가 실행되기까지 과정
- 프로그래머가 자바
.java파일 을 작성한다.
- javac compiler에 의해
.java → .class 파일로 컴파일 된다.
- build란? 간단히 말해 build = compile + linking 과정이다. build란 compile - packaging - testing - distribution 과정으로 구성되어있다. 위의 compile이 빌드 단계중 하나이다. 대표적인 build tool로는 maven, gradle, ant가 있다.
.class 파일은 JVM이 이해할 수 있는 바이트 코드로 구성되어있다. 바이트 코드로만은 실행이 불가하다.
.java → .class로 컴파일했다라는것은 문법검사는 마쳤다의 의미도 내포됨, 시간 단축
- 하지만 소스코드 변경 시 마다 컴파일 필요하지만 java의 모토로 보면 한번 컴파일로 어디서나 실행 시킬 수 있다.(one compile, run every where)
.class 파일은 Class Loader에 의해 JVM의 메모리 영역인 Runtime Data Area로 로딩
- Execution Engine에 의해
.class 파일 해석
- 인터프리터를 통해 메모리에 로드된 바이트 코드를 한줄 씩 해석하고 실행함
- JIT(Just-In-TIme)컴파일러를 통해 바이트 코드를 컴퓨터가 이해할 수 있게 기계어로 바꾸는 작업
- Engine에 의해 기계어로 해석된 것들이 Runtime Data Area에 배치되어 쓰레드 동기화나 GC를 수행하게 됨
- 실행 준비가 완료되면 JVM은 main 메서드(entry point)를 호출
- 호출된 main 메서드를 실행할 메인 스레드가 생성되며 메인 스레드의 JVM stack이 생성됨
- 앱이 실행되며 필요한 시점 마다 필요한 처리 및 메모리 확보 데이터 저장
왜 런타임까지 컴파일을 미룰까?
아래와 같이 추론해 봤습니다.
- "Write once, run anywhere” 자바의 모토에 따라 javac에 의해 컴파일된 바이트 코드만 있다면 어떠한 플랫폼에 맞게 구현된 JVM에 가져가서 해석될 수 있기 때문이다. 이는 플랫폼에 종속되지 않고 독립성을 가질 수 있다.
- 파이썬은 코드를 전달한 뒤, 직접 실행해야 하지만 자바의경우 이미 컴파일한 바이트 코드(.class파일)만 전달 하면되기때문에 직접적인 코드 공개가 되지 않고 이미 컴파일이 되었다는것은 문법 검사가 완료되었다는것을 보장한다.
- JIT 컴파일러 특성 상 자주 사용되는 코드를 분석하여 미리 컴파일 한다고 한다. 이 특성을 살려 미리 문법 검사를 마친 바이트 코드가 있다면, 매번 컴파일 하지 않고 어플리케이션 실행 성능에 도움이 되지 않을까 생각이 든다.
객체의 Lifecycle
-
Created (생성)
객체를 heap 메모리에 할당하고, 생성자 호출 및 인스턴스 변수 초기화를 한다.
Dog dog = new Dog();
-
In use or reachable ( 사용중 )
참조중인 객체를 뜻한다. 생성된 객체의 참조변수를 통해 객체를 사용한다.
객체가 참조중일때, GC대상이 되지 않는다.
-
Invisible ( 사용 중이며 접근불가 )
프로그램 상에서 더이상 객체에 대한 참조가 존재하지 않아 접근할 수 없을때
모든 객체가 이 단계를 거치는것은 아니다.
아래 코드에서 try문을 지나면 foo 참조변수에 대해서 더이상 접근 할 수 없다.
GC의 대상이 아니기 때문에 메모리 누수의 원인이 될 수 있다.
try {
Object foo = new Object();
foo.doSomething();
} catch (Exception e) {
}
GC대상이 되기 위해 try 블럭 위에서 객체를 선언하는것이 좋다.
Object foo = null;
try {
foo = new Object();
foo.doSomething();
} catch (Exception e) {
}
-
Unreachable ( 사용되지 않음 )
더 이상 객체에 대한 참조가 되지 않는 경우
GC 후보 대상이 된다. JVM은 해당 객체가 점유하는 메모리가 꼭 필요하지 않다면 GC작업을 늦출 수 있다.
Dog dog = new Dog();
dog = null;
-
Collected ( GC 대상이 되는 상태)
메모리 해제 직전 단계. 객체가 finalize 메소드(Resource를 정리)를 가지고 있다면, 메모리 해제 전에 수행한다.
-
Finalized ( Finalize 를 거친 상태 )
객체의 finalized()가 호출되어 unreachable 상태
-
Deallocated (메모리 해제 된 상태)
메모리 반환이 끝난 상태로 GC동작 마무리된 상태
GC
Heap영역의 물리적 공간 Young / Old
https://journaldev.nyc3.digitaloceanspaces.com/2014/05/Java-Memory-Model.png
- Young generation = Eden + S0(Survival0), S1(Survival1)
- 대부분의 객체가 금방 접근 불가능 상태이므로 많은 객체가 여기서 생성되었다가 사라진다.
- Young generation에서 객체가 사라질 때 Minor GC(Minor Garbage Collection)가 발생한다고 표현한다.
- Old generation
- Young영역에서 살아남은 객체가 복사되는 곳
- Young영역보다는 GC가 적게 발생한다.
- Old generation에서 객체가 사라질 때 Major GC(Major Garbage Collection)가 발생한다고 표현한다.
- Perm 영역 (Method Area)
- intern된 문자열 정보를 저장하는곳
- 일반적으로 GC대상은 아니지만 문자열 참조대상이 없는 경우 GC가 발생. Major GC로 포함된다.
- 단, 동적으로 생성된 문자열은 여기에 포함되지 않는다.
Heap 메모리 동작 과정
- 새로운 객체가 Eden영역에 생성
- Eden영역의 메모리가 꽉 차면 GC 한번 발생 후 살아있는 객체만 Survivor영역으로 복사
- Survivor영역이 꽉차면 S1영역으로 객체가 복사된다.
STW - Memory fragmentation
- stop-the-world란? gc를 실행하기위해 jvm의 어플리케이션 실행을 멈추는것 stop-the-world 발생 시 스레드를 제외한 나머지 스레드는 모두 작업을 멈춘다.
그리하여 stop-the-world가 길어 질수록 성능이 떨어지게 된다. 이러한 단점에도 stop-the-world상태로 GC처리가 되는것인가?
메모리 파편화
- 메모리 공간을 할당하고 해제하는 과정에서 발생하는 현상
- 외부 파편화
- 메모리 공간이 여러개로 나뉘어서 사용되어, 충분한 메모리 공간이 있어도 큰 메모리 블록을 할당할 수 없는 경우
- 내부 파편화
- 메모리 할당 후 남는 공간이 발생하는 경우
- 메모리를 효율적으로 사용하지 못하게 됨
→ GC 동작 시 이를 해결하기 위해 Compaction이 수행되어야한다. 이를 위해 stop-the-world가 발생해야한다. 또한 메모리 해제 및 Compaction이 진행 중 어플리케이션 작업 수행으로 인해 메모리가 변경되어 GC작업이 방해가 될 수 있기때문이다. 이러한 이유로 GC작업 시 stop-the-world가 필요하다.
GC 대상이 되는 조건 (GC Root)
GC수행 시 Mark and Sweep이라는 동작이 있다.
- Mark and Sweep이란?
- Garbage Collection 작업 시 참조하는 객체 마킹
- Marking작업이 끝난 후 Marking되지 않은 객체 해제
Mark and Sweep 동작은 GC Root로 부터 시작된다.
GC Root란, Garbage Collection의 root, root는 다른 object를 참조하여 객체들을 탐험할 수 있게 연결되어있다.

https://sihyung92.oopy.io/java/garbage-collect/1
Root로 부터 시작되서 Mark and Sweep을 진행한다.
GC Root가 될 수 있는 대상은 아래와 같다.
- 실행중인 쓰레드
- static 변수
- local 변수
- JNI 레퍼런스
Mark과정이 끝나면 Garbage Collector는 Heap영역을 돌면서 Mark되지 않은 메모리를 해제한다.이를 Sweep이라고 한다.
[참고]
https://hodongman.github.io/2019/11/30/Java-Finalize().html
https://himanshugpt.wordpress.com/2010/03/17/life-cycle-of-java-object/
https://gominhaja.tistory.com/25
https://golf-dev.tistory.com/68
https://velog.io/@cham/JAVA-GCGarbage-Collector
https://sihyung92.oopy.io/java/garbage-collect/1