3일 전

이 글에서는 프로그램을 작동시킬 때 객체가 메모리상에서 정확히 어떻게 저장되는지 구현 방식을 설명해보겠습니다. 로우레벨(CPU/GPU/메모리) 관점에서 코드를 바닥까지 이해하는 것은 언제나 도움이 된다고 생각해요.
이후 객체의 생성과 관리가 코드 설계에서 중요한 이유까지 짤막하게 설명드리겠습니다.
운영체제와 컴퓨터 아키텍처에 관한 지식이 있으시면 좀 더 수월하게 읽으실 수 있습니다
프로그램을 실행한다는 것은 CPU가 작성한 코드대로 행동하도록 만든다는 것입니다. 우리가 흔히 작성하는 코드들은 HDD같은 2차 저장장치에 저장됩니다. 하지만 CPU는 데이터를 메모리와만 주고 받을 수 있습니다. 결국 어떤 코드를 작동시키기 위해서는 그 코드 데이터의 기계어 형태를 메모리에 업로드해야 합니다.
어떤 프로그램이 메모리에 할당이 되면 다음 그림과 같은 구조로 메모리가 할당이 됩니다.

Java의 경우 JVM 위에서 실행되기 때문에, 일반적인 구조에 더해 Method 영역이 존재합니다.
Java의 중요한 특징은 객체가 저장되는 방식입니다.
객체는 항상 Heap 영역에 생성되며, Stack에는 객체 자체가 아닌 해당 객체를 가리키는 참조값만 저장됩니다.
또한 클래스 변수(static 변수)는 Heap이 아닌 Method 영역에 저장됩니다. 이는 JVM이 클래스 단위로 데이터를 관리하기 때문입니다.
이를 정리하면 다음과 같이 생각할 수 있습니다:
이러한 구조는 객체의 생명주기를 Heap 중심으로 통일하고, Garbage Collector를 통한 자동 메모리 관리를 가능하게 합니다.
이를 정리하면 다음 그림처럼 데이터가 저장됩니다.

Java에서 객체는 Heap 영역에 다음과 같은 구조로 저장됩니다:
[ 객체 헤더 ] [ 인스턴스 필드 데이터 ]
객체 헤더에는 다음과 같은 정보가 포함됩니다:
그 아래에는 우리가 정의한 필드 값들이 순서대로 저장됩니다.
예를 들어 다음과 같은 클래스가 있을 때:
class Person {
int age;
int height;
}객체는 메모리 상에서 다음과 같이 표현됩니다:
[ 헤더 ] [ age ] [ height ]
메모리 구조상 각각의 객체는 하나의 프레임형식으로 heap에 블록단위로 저장됩니다. 이런 구현상 원리에 의해 각각의 객체는 독립적으로 존재합니다.
이런 메모리상 구현 방식을 바탕으로 왜 객체 관리와 설계가 중요한지 생각해보겠습니다.
Java에서는 Garbage Collector가 Heap영역을 관리합니다. 명시적으로 동적 할당된 데이터를 할당 해제하는 것이 불가능 하기 때문에 불필요한 메모리 누수가 발생할 수 있고, 이는 작동환경에 따라서 오류와 성능저하를 유발할 수 있습니다.
Engine e = new Engine();
c1.engine = e;
c2.engine = e;위 코드를 생각해봅시다. e에 해당하는 객체 데이터덩어리 하나는 Heap에 저장돼있습니다. 이때, c1.engine과 c2.engine의 stack 값에는 이 같은 heap 데이터 덩어리 주소가 저장돼있습니다. 즉 e가 변형되면 상태가 의도와 다르게 오염될 수 있습니다.
댓글 0