“코드를 작성하고 실행 버튼을 눌렀을 뿐인데, 그 안에서는 무슨 일이 일어날까?”
개발을 시작하면 우리는 보통 IDE(통합개발환경)에서 코드를 작성하고, 저장하고, 실행 버튼을 누릅니다. 하지만 우리가 작성한 그 코드가 어떻게 실제로 프로그램으로 동작하는 걸까요? 이 글에서는 그 과정의 이면을 들여다보며, 특히 Java 언어를 중심으로 JVM 내부의 세계와 메모리의 구체적인 동작 방식을 이야기해보려 합니다.
.java)우리는 보통 이렇게 코드를 작성합니다:
public class Hello {
public static void main(String[] args) {
System.out.println("Hello, world!");
}
}이 코드는 .java 확장자를 가진 소스 코드 파일입니다. 하지만 이 파일은 CPU가 직접 이해하지 못합니다. 우리는 컴파일이라는 과정을 거쳐야 합니다.
.class)Java 컴파일러(javac)는 이 .java 파일을 읽어 들여 바이트코드(Bytecode) 로 변환합니다.
javac Hello.java결과로 Hello.class라는 파일이 생성되며, 이 파일은 JVM만 이해할 수 있는 중간 언어입니다.
바이트코드는 JVM(Java Virtual Machine)에 의해 해석되어 실행됩니다.
java Hello실행하면 JVM이 해당 .class 파일을 로딩하고, 내부적으로 메모리를 할당하고, 클래스 로딩을 수행하며, 결국 main() 메서드를 찾아 실행하게 됩니다.
JVM은 단순한 인터프리터가 아닙니다. 그 안에는 아주 복잡하고 정교한 구조가 존재하죠. 핵심 구성 요소는 다음과 같습니다:
+-----------------------------+
| Class Loader Subsystem |
+-----------------------------+
| Runtime Data Area |
| - Method Area |
| - Heap |
| - Java Stack |
| - PC Register |
| - Native Method Stack |
+-----------------------------+
| Execution Engine |
| - Interpreter |
| - JIT Compiler |
+-----------------------------+JVM이 실행되면 다음과 같은 메모리 영역들이 생성됩니다.
static 변수나 클래스 메타데이터가 여기 저장됨Person p = new Person(); // 힙에 저장Java는 메모리 관리를 자동으로 해줍니다. 그 중심에는 GC(Garbage Collector) 가 있죠.
G1 GC가 기본 채택됨JVM은 멀티스레딩을 지원합니다. 각 스레드는 독립적인 스택을 가지며, 힙 영역을 공유합니다. 이를 통해 동시성과 병렬 처리가 가능하지만, 동기화 문제나 데드락 등의 이슈도 함께 생길 수 있죠.
synchronized(obj) {
// 한 번에 한 스레드만 접근 가능
}JVM이 모든 작업을 마치면, 스택 영역과 힙 영역은 GC 또는 JVM 종료와 함께 정리됩니다. 우리가 쓴 System.exit() 나 main() 종료는 곧 프로그램의 생명 주기의 끝을 의미합니다.
많은 초보 개발자들은 “코드를 잘 짜는 것”을 문법이나 라이브러리의 사용법으로 이해합니다. 그러나 진짜 실력은 코드가 어떻게 실행되는지를 이해하는 것에서 나옵니다.
이런 것들을 이해하고 나면, 코드가 단순히 “돌아가기만” 하는 것이 아니라, 어떻게 잘 돌아가게 할 것인가에 대한 통찰을 얻을 수 있습니다.
💡 “눈에 보이지 않는 실행 흐름까지 이해할 때, 우리는 진짜 개발자가 된다.”
