JVM Stack & Heap
자바와 C/C++의 차이점
C/C++은 네이티브 코드로 컴파일되며, 운영체제(OS)와 CPU 아키텍처에 맞게 컴파일된 코드만 특정 플랫폼에서 실행된다. 예를 들어 리눅스에서 컴파일된 C/C++ 프로그램은 윈도우에서 바로 실행되지 않는다. 따라서 각 플랫폼에 맞는 컴파일 과정이 필요하며 이를 "타겟 플랫폼 컴파일"이라고 한다.
반면, 자바는 자바 소스 코드를 자바 컴파일러(javac)로 컴파일하면 운영체제에 의존하지 않는 바이트코드(Bytecode)가 생성된다. 이 바이트코드는 다양한 운영체제에서 실행될 수 있다. 이처럼 자바는 JVM(Java Virtual Machine)을 통해 플랫폼 독립성을 갖추게 된다.
JVM(Java Virtual Machine)의 역할
자바 바이트코드는 JVM이라는 가상 머신에서 실행된다. JVM은 운영체제 및 CPU 아키텍처와 상관없이 자바 바이트코드를 이해하고 실행할 수 있도록 해준다. 이로 인해, 자바 프로그램은 "한 번 컴파일, 어디서나 실행"이라는 철학을 구현하게 된다.
JVM은 각 플랫폼에 맞게 설치되어 있으며 운영체제와 CPU의 특성에 맞게 바이트코드를 해석하고 실행한다. 자바의 이러한 특징 덕분에 동일한 자바 프로그램이 윈도우, 리눅스, 맥 등 다양한 플랫폼에서 실행될 수 있다.
JVM 메모리 구조 : Stack과 Heap
Heap(힙)
힙 메모리는 JVM에서 생성된 모든 객체가 저장되는 공간이다. 이는 모든 쓰레드가 공유하는 메모리 영역이다. 자바 객체와 클래스의 인스턴스는 힙 메모리에 저장된다.
힙은 프로그램이 실행되면서 생성된 객체와 배열 등을 관리하며 필요에 따라 Garbage Collector가 사용되지 않는 객체를 정리해준다.
Stack(스택)
스택 메모리는 각 쓰레드마다 독립적으로 할당됩니다. 자바 프로그램에서 메소드가 호출될 때마다 프레임(Frame)이 생성되며 이 프레임에는 해당 메소드의 지역 변수와 임시 계산 값이 저장된다. 메소드가 실행을 마치면 프레임이 스택에서 제거된다.
스택은 LIFO(Last In, First Out) 구조로 메소드 호출 시마다 새 프레임이 추가되며 메소드가 종료될 때 해당 프레임이 제거된다.
스택은 빠르게 액세스할 수 있는 메모리 공간으로 프로그램의 흐름을 제어하는 데 사용된다.
JIT(Just-In-Time) 컴파일러와 성능 최적화
자바는 초기에 바이트코드로 컴파일되지만 실행 중에는 JIT 컴파일러가 바이트코드를 네이티브 코드로 변환합니다. JIT 컴파일러는 실행 시점에 프로그램을 분석하고 실제로 사용하는 코드에 대해 최적화를 수행한다. 이 과정에서 성능 향상을 위한 다양한 최적화가 이루어진다.
이 방식은 프로그램 실행 중에 발생하는 중요한 정보를 기반으로 최적화를 진행하므로 처음부터 네이티브 코드를 생성하는 C/C++와 달리, 실행 시점에 더 유연한 최적화를 할 수 있다.
자바의 스택 기반 아키텍처
자바는 스택 기반의 아키텍처를 채택하고 있다. 이는 레지스터 기반 아키텍처에 비해 플랫폼 독립성을 더 잘 유지할 수 있도록 설계된 것이다.
레지스터 기반 아키텍처에서는 CPU가 사용하는 레지스터의 개수나 구조가 플랫폼마다 다르기 때문에, 프로그램의 이식성이 떨어질 수 있다. 반면, 자바는 스택을 이용하여 모든 연산을 처리하므로 하드웨어 레지스터와 무관하게 프로그램이 동작할 수 있다.
이는 자바가 다양한 기기와 운영체제에서 동일하게 동작할 수 있도록 만든 중요한 요소이다.
자바의 플랫폼 독립성과 네트워크 시대
자바가 등장한 시기는 인터넷과 네트워크가 확장되던 시기였다. 당시에는 다양한 플랫폼(PC, 서버, 모바일 기기 등)에서 동일한 프로그램을 실행해야 할 필요성이 커지고 있었으며 자바는 이 문제를 해결할 수 있었다.
자바의 플랫폼 독립성 덕분에 자바로 작성된 프로그램은 네트워크를 통해 전달되어 각기 다른 플랫폼에서 동일하게 실행될 수 있다. 자바는 이런 특성 덕분에 네트워크 환경에서 매우 유용한 언어로 자리 잡았다.
결론
- 자바는 JVM을 통해 플랫폼 독립성을 확보하여 다양한 운영체제와 CPU에서 동일한 프로그램을 실행할 수 있는 강력한 이점을 가진다.
- JVM의 메모리 구조는 힙과 스택으로 나뉘어 있으며 각각 객체와 메소드 실행 정보를 관리한다.
- 자바는 스택 기반 아키텍처를 채택하여 레지스터 의존성을 최소화하였고 JIT 컴파일러를 통해 실행 시점 최적화를 통해 성능을 향상시킨다.
- 자바의 이러한 설계는 자바가 네트워크 시대에 강력한 도구로 자리 잡게 된 중요한 이유이다.