인생을 코딩하다.

[Java] Call by value의 메모리 관리 과정 본문

Java

[Java] Call by value의 메모리 관리 과정

Hyung1 2021. 1. 9. 09:37
728x90
반응형
public class ReferencePass {
    public static void main(String[] args) {
        ReferencePass reference = new ReferencePass();
        reference.callPassByValue();
    }
    
    public void callPassByValue() {
        int a = 10;
        String b = "hyungil";
        
        System.out.println("before passByValue");
        System.out.println("a=" + a); 
        System.out.println("b=" + b); 
        
        passByValue(a, b);
        
        System.out.println("after passByValue");
        System.out.println("a=" + a);
        System.out.println("b=" + b);
        
    }
    
    public void passByValue(int a, String b) {
        a = 20;
        b = "z";
        System.out.println("in passByValue");
        System.out.println("a=" + a);
        System.out.println("b=" + b);
    }
}

위 코드는 도서 "자바의신" p.208에 있는 코드다. 책에도 설명이 잘 나와있지만, 메모리 관리가 이루어지는 과정으로는 설명이 빈약한게 아쉽다는 생각이 들었다. 혹시나 자바의 신을 보다가 구글링하며 이 글을 보았을때 도움이 되길 바라며.. 더 깊은 이해를 위해 Stack & Heap 영역의 역할에 기반하여 위 코드를 설명해본다.

 

위의 코드를 컴파일 하고 실행하면 어떻게 될까?

before passByValue
a = 10
b = hyungil
in passBycalue
a = 20
b = z
after passByValue
a = 10
b = hyungil

가 출력된다.

 

우선 책에서의 설명은

 

이렇다.

 

메모리 구조 공부한 것을 정리도 할 겸 위의 사진을 토대로 덧붙여 추가 설명을 하자면,

int a = 10;

STACK에 a라는 변수명으로 공간이 할당되고 a의 변수 타입은 원시타입(primitive types)입 이기 때문에 이 공간에는 실제로 10라는 값이 할당된다.
이때 원시 원시 타입 데이터에 관해서는 참조 값이 아니라 실제 값이 저장된다.

우측 큰 박스가 heap입니다.

 

 

String은 Object를 상속받아 구현되었다. 그래서 String은 heap 영역에 할당 되고 b라는 이름으로 생성된 변수는 stack에 할당된다. b는 "hyungil"라는 스트링을 참조하게 된다. 그림으로 표현하면 아래와 같다.

 

 

callPassByValue()메서드의

passByValue(a,b);

에 의해 passByValue()가 호출 된다. 호출할때 인자로 a변수를 넘겨주면서 scope가 passbyValue() 함수로 이동한다. scope가 바뀜에 따라 기존 a는 scope에서 벗어나게 되고 사용할 수 없게 된다. 이때 인자로 넘겨받은 a에 복사되어 전달되는데 a 또한 원시타입이므로 stack에 할당된 공간에 값이 할당된다.

 

 

 

그 후, 호출할때 인자로 b변수를 넘겨주면서 scope가 passbyValue() 함수로 이동한다. 기존 b가 인자로 받은 b에 복사되고 Stack에 전달된다. scope가 바뀜에 따라 기존 b는 scope에서 벗어나게 되고 사용할 수 없게 된다. 이때 새로운 스트링 오브젝트 "z"가 heap영역에 할당된다. 그리고 STACK 새로운 공간을 할당받은 b는 새롭게 생성된 "z"를 참조하게 된다.

 

 

 

이제 함수가 중괄호 } 에 도달하여 종료되면 passByValue() 함수의 지역변수는 모두 STACK에서 pop되어 사라지고, 힙영역은 그대로인 상태로 함수 호출이 종료된다.

 

그러면 원상태로 돌아오게 되고 다시 상태는 아래와 같이 된다.

 

 

하지만 이렇게 되면 기존의 "z" 라는 문자열을 참조하고 있는 변수는 아무것도 없으므로 Unreachable Object가 된다.

 

JVM의 GC가 일어나면 첫번째 과정은 스택의 모든 변수를 스캔하면서 변수들이 어떤 오브젝트를 참조하고 있는지 찾는다. 그 후 Unreachable Obbject를 우선적으로 메모리에서 제거한 후 메모리 공간을 확보한다.

그래서 이런 경우에 GC에 의해 Unreachable Object들은 메모리에서 제거된다.

(GC는 Garbage Collection을 뜻하고, Unreachable Object는 Stack에서 참조할 수 없는 Heap 영역의 객체를 말한다.)

 

여기서 GC는 garbage를 수집하여 제거하는 것 같지만, 사실 garbage가 아닌 것들을 따로 mark하고, mark되지 않은 것들은 모두 지운다.

 

그래서 GC가 일어난 후의 메모리 상태는 다시 아래와 같이 된다.

 

 

참고문헌 : 이상민 "자바의신"


 

 

 

728x90
반응형

'Java' 카테고리의 다른 글

[Java] Collection - List  (0) 2021.01.13
[Java] Collection  (0) 2021.01.13
[Java] 열거형(enum)  (0) 2021.01.05
[Java] Serializable과 NIO  (0) 2020.11.25
[Java] 보조 스트림  (0) 2020.11.24
Comments