[Java] Stack / Heap

2023. 1. 12. 17:45기술 창고/Java

728x90
SMALL

Stack

- 객체 타입 참조값 / 원시 타입 데이터들 할당

Heap 영역에 생성된 객체 타입의 데이터들에 대한 참조 값, 원시 타입의 데이터들(byte, short, int, long, double, float, boolean, char) 들이 할당되어 저장된다.

원시 타입의 데이터들의 경우에는 참조값을 저장하는 것이 아니라 저장된 변수 실제 값을 저장하게 된다.

# 예 : int = 5 , 5가 저장 

 

- Stack 영역에 있는 변수들은 visibility 를 가진다.

전역변수가 아닌 지역변수가 특정 함수내에서 Stack 에 할당된 경우, 해당 지역변수는 다른 함수에서 접근할 수 없다.

예를 들어, One() 이라는 함수에서 Two() 함수를 호출하고 Two() 함수의 종료되는 중괄호 } 가 실행된 경우 (함수가 종료된 경우), Two() 함수 내부에서 선언한 모든 지역변수들은 stack 에서 pop 되어 사라진다.

 

- Stack 메모리는 Thread 하나당 하나씩 할당된다.

즉, 스레드 하나가 새롭게 생성되는 순간 해당 스레드를 위한 stack 도 함께 생성되며, 각 스레드에서 다른 스레드의 stack 영역에는 접근할 수 없다.

 

 

Stack 코드 설명

public class Main {
    public static void main(String[] args) {
        int argument = 4;
        argument = someOperation(argument);
    }

    private static int someOperation(int param){
        int tmp = param * 3;
        int result = tmp / 2;
        return result;
    }
}

arument 변수에 4 라는 값이 할당되었다.

int 타입의 변수로서 단순 원시타입의 변수라는 것을 알 수 있다.

따라서 실제 값 4가 Stack에 저장된다.

 

# Stack에 변수와 실제값 4 저장

 

someOperation 함수를 호출하였고, 인자값으로 argument 변수를 넣어주었다.

따라서, scope 가 someOperation 함수로 이동하게 되고, scope가 이동하게 되면서 기존의 argument 변수 값을 사용할 수 없게 된다.

이전에 다뤘던 내용처럼 Call by Value 형식으로 단순 변수 값을 매개로 함수를 호출하였기 때문에 argument 변수와 someOperation 함수에 넘어온 param 변수는 다르다고 볼 수 있다.

이제 넘어온 param 변수가 Stack에 할당되게 되었다.

 

# 초기 argument 변수와 함수로 호출되어 넘어온 param 변수

 

그 다음 someOperation 함수 내의 int 타입 변수 tmp가 Stack에 우선적으로 계산되어 할당되고, 그 이후 result 변수도 계산되어 Stack에 할당된다.

 

# someOperation 함수 내에 지역변수로 생성된 변수들 Stack에 할당

 

지역 변수들의 Stack할당이 끝나고 최종적으로 함수가 끝나게 되면 호출 함수 scope에서 사용되었던 모든 지역변수들은 Stack에서 pop된다.

따라서, result, tmp, param 변수들은 pop되어 사라지게 되고 다시 argument 변수에 scope가 옮겨져서 바라보게 된다.

 

# 지역변수 pop , 최종적으로 argument 변수 scope 이동

처음에는 4의 값을 할당되었지만 함수가 실행되고 결과값 6이 반환됨으로서 다시 argument 에 6이 재할당되게 된다.

또한, main 함수도 종료되는 순간 Stack에 있던 모든 데이터들은 pop되어 프로그램이 종료되게 된다.

메모리가 자동으로 정리되는 것은 Garbage Collector의 역할이 있을 것이다.

 

 

 

Heap

- Stack에 저장되는 데이터들보다 큰 데이터들 저장

Heap 영역에는 주로 긴 생명주기를 가지는 데이터나 크기가 큰 데이터들이 저장된다.

따라서, 객체 타입의 데이터들이 저장된다.

또한, Stack에 있는 데이터들을 제외한 모든 부분을 포함하는 영역이다.

 

- 객체 타입

앞서 언급했었던 것처럼 객체 타입의 데이터들이 저장된다.

Integer, String, ArrayList 등등이 그 대표적인 데이터들이다.

 

- 하나의 Heap 영역 존재

몇 개의 스레드가 존재하든지 간에 단 하나의 Heap 영역만이 존재한다.

Stack은 하나의 스레드 당 하나를 할당받기 때문에 다르다.

 

- Stack에 Heap 영역에 있는 객체들 참조값 할당

Heap에는 객체 데이터가 저장되어있고, Stack은 각 객체에 따른 참조값을 할당받는다.

 

 

Heap 코드 설명

public class Main {
    public static void main(String[] args) {
        int port = 4000;
        String host = "localhost";
    }
}

int 타입의 port 변수는 실제값까지 Stack에 바로 할당될 것이다.

String 타입의 host 변수의 경우에는 객체타입이기때문에 Heap영역에 "localhost" 데이터가 저장, Stack에는 "localhost"를 참조하는 주소값을 할당받게 된다.

 

 


Stack / Heap 심화 코드 설명

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> listArgument = new ArrayList<>();
        listArgument.add("yaboong");
        listArgument.add("github");

        callfunction(listArgument);
    }

    private static void callfunction(List<String> listParam) {
        String value = listParam.get(0);
        listParam.add("io");
        System.out.println(value);
    }
}

처음에 List 객체 타입 변수를 생성해주었다.

여기서 new 키워드는 생성하려는 객체를 저장할 수 있는 공간이 충분한지 Heap영역에서 찾아본 다음, 충분한 것을 확인하고 빈 List를 참조하는 listArgument 변수를 Stack에 할당한다.

따라서 listArgument 라는 객체 참조 값은 Stack으로, 빈 List 객체 데이터는 Heap영역에 할당된다.

 

# List 변수 Stack, Heap 영역 할당

 

listArgument.add() 명령어를 두번 실행한다.

이는 listArgument.add(new String("yaboong")), listArgument.add(new String("github")) 라고 표현할 수도 있다.

따라서 List 객체에 String 객체 타입 데이터 두개를 넣은 것이다.

추가된 String 객체들은 그대로 listArgument 변수에서 추가된 것이므로 Stack에는 listArgument 가 그대로 할당된 상태이며, 추가된 String 객체 타입 변수들이 Heap 영역에 할당되게 된다.

 

# Heap 영역에 할당된 String 타입 객체들

 

다음으로 callfunction 함수를 listArgument 변수를 인자값으로 호출하게 되면, listParam이라는 지역변수로서 새로 Stack 에 할당되고 Heap 영역에 참조하고 있는 데이터는 그대로인 상태로 구성되게 된다.

그리고 함수호출로서 scope가 이동되었기 때문에 기존의 listArgument 변수는 일단 접근할 수 없게 된다.

 

# listParam 지역변수 새로 할당

 

callfunction 함수 내에서 String 객체 변수 value가 생성이 되고 Stack에 value 참조값을 할당, Heap에 listParam 변수의 0번째 참조 데이터를 할당한다.

또한, listParam.add("io") 를 통하여 List 객체에 새로운 String 객체 타입 변수를 추가해줌으로서 Heap 영역에 추가로 할당해준다.

 

# value 지역 변수 생성 및 할당, listParam 객체 추가 할당

 

이제 호출된 callfunction 함수가 종료가 되게 되면 생성되었던 지역변수들은 pop되어 사라지게 되고, 기존에 있었던 listArgument 변수로 scope가 옮겨져 바라보게 된다.

하지만 listParam과 listArgument 는 동일한 객체 타입을 참조하고 있으므로 listArgument는 pop되어 사라지지 않는다.

 

# 지역 변수 pop

여기서 객체 타입의 데이터, 즉 Heap 영역에 있는 데이터는 호출 함수 내부에서 파라미터로 copied value 를 받아서 변경하더라도 함수호출이 종료된 시점에 변경내역이 반영되는 것을 볼 수 있다.

728x90
반응형
LIST