Programming/Java

Static, Stack, Heap / Java 메모리 영역의 구조와 특징

Jan92 2021. 8. 23. 22:43

static, stack, heap

 

Java를 사용하면서 알아야 할 메모리 구조 및 특징에 대해서 정리합니다. (Static, Stack, Heap)

 

먼저 프로그램을 구동하기 위해서는 운영체제(OS)가 메모리(RAM)에 데이터 및 명령어를 저장할 공간을 할당하여 줍니다. 메모리는 컴퓨터에 있어 가장 핵심이 되는 부품이고, CPU가 처리할 데이터가 임시로 저장되는 공간입니다. 동작은 하드디스크에 저장된 데이터가 메모리에 올라가서 실행되며, 메모리(RAM)를 주 기억 장치라고 부릅니다.

 

메모리는 사용할 수 있는 공간이 한정되어 있기 때문에 어떻게 관리하느냐에 따라서 프로그램의 성능(속도 등)이 좌우됩니다. 따라서 Java 어플리케이션에서 메모리를 효율적으로 사용하기 위해서는 메모리 구조와 특징에 대해 이해할 필요가 있습니다.

 

 

 


 

 

1. Static Area (스태틱 메모리 영역)

 

static(정적)은 고정된 이라는 의미를 가지고 있습니다. Static 이라는 키워드를 사용하여 Static 변수(정적 필드)와 Static 메서드(정적 메서드)를 만들 수 있는데, 두 가지를 합쳐서 정적 멤버라고 합니다. (= 클래스 멤버)

정적 필드와 정적 메소드는 객체(인스턴스)에 소속된 멤버가 아니라 클래스에 고정된 멤버입니다.

 

하나의 Java 파일은 크게 필드(field), 생성자(constructor), 메서드(method)로 구성되어 있고, Static Area(스태틱 메모리 영역)에서는 그중 필드 부분에서 선언된 변수(전역 변수)와 정적 멤버 변수(static이 붙은 자료형)의 데이터를 저장합니다.

 

Static Area(스태틱 메모리 영역)에 데이터는 프로그램의 시작부터 종료가 될 때까지 메모리에 남아있게 됩니다. 즉, 프로그램이 종료될 때까지 어디서든지 사용이 가능하지만, 주의할 점은 전역 변수를 무분별하게 많이 사용하게 되면 메모리가 부족한 문제가 발생할 수 있습니다. 따라서 필요한 변수만 전역 변수로 사용할 필요가 있습니다.

 

* 객체에 소속된 멤버가 아닌 클래스에 고정된 멤버

 

 

 


 

 

2. Stack Area (스택 메모리 영역)

 

메소드 내에서 정의하는 기본 자료형 (int, double, byte, long, boolean 등)에 해당되는 지역변수(매개 변수 및 블록 문 내의 변수 포함)의 데이터 값이 저장되는 공간이 Stack Area(스택 메모리 영역)입니다. 해당 메서드가 호출될 때 메모리에 할당되고, 종료되면 메모리에서 사라집니다.

 

기본형 타입 변수의 값들은 Stack 영역에 저장되고, 참조형 타입 변수는 참조값만 저장됩니다. 이 참조값은 아래서 설명할 Heap 영역에 존재하는 인스턴스(객체)를 가르키는 역할을 합니다. (인스턴스의 주소 값)

 

* 메서드 내에서 정의하는 기본 자료형

 

public void stackAreaExample() {
    int a = 5;	a = 4;	a = 3;	a = 2;
    System.out.println("a : " + a); // 출력된 값 => a : 2
        
    for(int i=0; i<5; i++){
    
    }
//  System.out.println(i); 컴파일 에러
}

 

a라는 int 타입의 변수는 stackAreaExample method를 호출했을 때 Stack 영역에 할당되고, 종료 시 해제됩니다.

 

a라는 변수의 값에 5, 4, 3, 2 순서로 값을 할당하였고, 결과적으로 2라는 값이 출력됩니다. 이는 Stack 영역이 LIFO(Last In First Out)의 구조(새로운 데이터가 할당되면 이전의 데이터는 지워지는 특징)를 갖기 때문입니다.

 

System.out.println(i); 부분에서 컴파일 오류가 발생하는 이유는 i는 for문 안에 있는 지역 변수이므로 for문이 시작되며 할당되고, 종료되면서 Stack 영역에서 해제되기 때문입니다.

 

 

* 참고

프로세스(Process)는 완벽히 독립적이기 때문에 메모리 영역 (Code, Data, Stack, Heap)을 다른 프로세스와 공유하지 않지만, 스레드(thread)는 프로세스 안에서 해당 스레드를 위한 Stack 영역을 생성하기 때문에 하나의 스레드에서 다른 스레드로의 접근은 불가능하지만 static, heap 영역에 저장된 데이터들은 공유해서 사용할 수 있습니다.

 

 

 


 

3. Heap Area (힙 메모리 영역)

 

참조형(Reference Type)의 데이터 타입을 갖는 객체, 배열 등은 Heap 영역에 데이터가 저장됩니다.

* 클래스 변수 = new 클래스(); 를 통해 생성된 객체(인스턴스)

 

이때 변수(객체, 객체 변수, 참조 변수)는 Stack 영역의 공간에서 실제 데이터가 저장된 Heap 영역의 참조값(Reference key, 해시 코드 = 메모리에 저장된 주소를 연결해주는 값)을 new 연산자를 통해서 리턴 받습니다.

다시 말하면 실제 데이터를 가지고 있는 Heap 영역의 주소 값을 Stack 영역의 객체가 갖고 있는 것입니다.

이렇게 리턴 받은 참조값을 가지고 있는 객체를 통해서만 해당 인스턴스를 컨트롤할 수 있습니다.

 

인스턴스의 실제 데이터는 Heap 영역에 올라갑니다. 저장된 메모리 위치가 다르기 때문에 static 메서드에서 외부 인스턴스 멤버에 접근할 수 없습니다.

 

* Heap은 참조형의 데이터 객체에 실제 데이터들이 담기는 공간이고, 실제 데이터를 가지고 있는 Heap 영역의 참조값을 Stack 영역의 객체가 가지고 있는 것

 

 

 


 

public void memoryArea {
    int a = 10;
    
    User user = new User();
}

 

int a = 10; 이라는 코드를 작성했다면, 정수 값이 할당될 수 있는 메모리 공간을 a라고 잡아두고, 그 메모리 영역에 10이라는 값이 들어갑니다. 즉 Stack 영역의 메모리에 이름을 a라고 붙여주고 값이 10인 메모리 공간을 만드는 것입니다.

 

User user = new User(); 라는 객체를 생성했다면 user은 Stack 영역에 생성되고, new로 생성된 User 클래스의 인스턴스(실제 데이터)는 Heap 영역에 생성됩니다. 그리고 스택 영역에 생성된 user의 값으로 힙 영역에 있는 실제 데이터의 주소 값을 가지고 있습니다.

즉, Stack 영역의 user는 Heap 영역의 실제 데이터를 가리키고 있는 것입니다.

 

* new를 통해 인스턴스 객체를 생성했을 때, heap 영역에는 생성된 객체가 올라가고, Stack 영역에는 해당 객체에 대한 주소 값(Reference)이 저장

 

 

 

+ 추가

어떤 참조 변수도 Heap 영역에 있는 인스턴스를 참조하지 않게 된다면, GC(가비지 컬렉터)에 의해 메모리에서 사라지게 됩니다.

 

 

 

참고 자료

https://m.blog.naver.com/heartflow89/220954420688