☕️자바 𝗝𝗔𝗩𝗔

[java] 자바 메모리 구조(static, stack, heap)

비타민찌 2022. 11. 18. 15:27
728x90

프로그램을 구동하기 위해서 운영체제가 메모리에 데이터 및 명령어를 저장할 공간을 할당해준다.

메모리를 효율적으로 사용하기 위해, 메모리의 구성과 특징에 대해서 이해해보자.

 

Runtime Data Area

[1]

Static area || Class Area

메서드 영역, 클래스 영역, 코드 영역, 스태틱 영역으로 이루어져 있다.

하나의 JAVA 파일은 크게 필드(field), 생성자(constructor), 메소드(method)로 구성된다. 그중 필드 부분에서 선언된 변수(전역변수)와 정적 멤버변수(static이 붙은 자료형) Static 영역에 데이터를 저장한다. Static 영역의 데이터는 프로그램의 시작부터 종료가 될 때까지 메모리에 남아있게 된다. 다르게 말하면 전역변수가 프로그램이 종료될 때까지 어디서든 사용이 가능한 이유이기도 하다. 따라서 전역변수를 무분별하게 많이 사용하다 보면 메모리가 부족할 우려가 있어 필요한 변수만 사용할 필요가 있다.

JVM이 동작해서 클래스가 로딩될 때 생성

 

 

[2]

Stack (스택 메모리 영역) / 정적 할당

스택은 메서드 내에서 정의하는 기본 자료형 즉, int double long boolean 등에 해당되는 지역변수에 데이터 값이 저장되고, Heap 영역에 생성된 Object 타입의 데이터 참조 값이 저장된다.

 

메서드가 호출될때 메모리에 할당되고, 메서드 종료 시 메모리가 해제된다. 컴파일 타임 시 할당된다.

우리가 자주 사용하는, for문 내에서 정의한 int i의 값이 for문 종료 이후 사용할 수 없게 되는 것도 지역변수인 i가 for문의 종료와 함께 스택 영역에서 해제되었기 때문이다.

 

[3]

Heap area(힙 메모리 영역) / 동적 할당

참조형(Reference Type)의 데이터 타입을 갖는 객체, 배열 등은 Heap 영역에 데이터가 저장된다. (new 키워드로 생성된 객체와 배열이 저장)

이때 변수(객체, 객체변수, 참조변수)는 Stack 영역의 공간에서 실제 데이터가 저장된 Heap 영역의 참조값(reference value, 해시코드 / 메모리에 저장된 주소를 연결해주는 값)을 new 연산자를 통해 리턴 받는다. 실제 데이터를 갖고 있는 Heap 영역의 참조 값을 Stack 영역의 객체가 갖고 있다는, 더 쉽게 말해 메서드 영역에 로드된 클래스만 생성이 가능하다. 이렇게 리턴 받은 참조 값을 갖고 있는 객체를 통해서만 해당 인스턴스를 핸들 할 수 있다. 런타임시 할당된다.

GC의 주요 대상이며, 효율적인 GC를 위해 Eden, Suvivor1,2, Old로 분리되어 있다. (위의 스택과 클래스 메모리도 대상이 된다.)

* 컴파일: 소스코드가 기계어로 변환되어 실행가능한 프로그램이 되는 과정(Systenx Error, 파일참조 에러, 타입체크 에러등)

* 런타임: 컴파일 타임 후 프로그램이 실행될 때(Null 참조 에러, 메모리부족 등)

 

예제를 통해 스택과 힙 메모리 영역을 코드로 확인해 보자.

 

[ Stack ]

public class Memory {
    public static void main(String[] args) {
        int a;
        m1();
    }

    private static void m1(){
        int b;
        double y;
        m2();
    }

    private static void m2(){
        int c;
        boolean z;
    }
}

메인 메서드를 실행하면, 스택 메모리 영역에 메인 메서드가 할당된다.

이를 그림으로 보면 가장 먼저 main 프레임이 생기고, m1, m2가 쌓인다.

메서드가 종료되면, 가장 최근에 호출된 순서대로 stack 메모리 영역에서 할당이 제거된다.

 

 

 

 

메서드 종료

 

이렇게 나중에 들어오고 먼저 지워지는 메모리 구조를 후입선출이라 한다.

 

 

[ Heap ]

public class Memory {
    public static void main(String[] args) {
        int a;
        m1();
    }

    private static void m1(){
        int b;
        double y;
        m2();
    }

    private static void m2(){
        int c;
        boolean z;
    }

    private static void m3(){
        String str = "힙";
        str += "추가";
    }
}

만약 m2 메서드가 m3 메서드를 호출했다면?

 

 

이 str 변수는 다른 int, double 과는 다르게 값을 가지고 있지 않다.

이 object에 구현되는 String은 Heap 영역에 생성된다.

 

str 변수는 heap 영역에 있는 string '힙'을

참조할 수 있는 주소값만 가지고 있는 것이다.

    private static void m3(){
        String str = "힙";
        str += "추가";
    }

새로운 객체를 참조하고

기존의 객체와의 연결은 끊어진다.

 

스택 메모리는 메서드가 종료됨과 동시에 메모리가 제거됐는데

힙 영역은 그렇지 않고 계속 할당되어 있다. 이로 인한 메모리 누수 문제 때문에 자바 unreachable object (어떤한 변수도 레퍼런스를 하고 있지 않은 객체가 있으면 unreachable object 라고 해서, GC가 unreachable object를 우선적으로 정리한다.

 

스택

-장점 : 액세스가 빠르다.
-단점 : 메모리 크기를 변경할 수 없다. (컴파일 타임에 크기가 결정됨)

 

[4]

PC Register

스레드가 생성될 때마다 생성되며, JVM이 현재 수행할 명령어의 주소를 저장하는 영역.

 

[5]

Native Method Stack

자바 외 언어로 작성된 네이티브 코드를 위한 메모리 영역

 

 

* 멀티스레드 시 공유되는 메모리 영역은?

1,2 번 메소드 영역, 힙 영역은 모든 스레드가 공유한다
3,4,5 는 각각의 스레드 마다 생성되고 공유되지 않는다.

 

 

 

 

참고:

https://cloudstudying.kr/lectures/287

https://lazaros7.tistory.com/436

https://velog.io/@ditt/JavaJVM-%EB%A9%94%EB%AA%A8%EB%A6%AC%EC%98%81%EC%97%AD  https://m.blog.naver.com/heartflow89/220954420688

728x90