JVM 런타임 데이터 영역으로 알아보는 Pass by value와 Pass by reference

이준혁·2023년 7월 16일
2

🤨변수는 어디에 저장될까?

Pass by value는 값을 전달, Pass by reference는 주소를 전달... 이해는 되는데 확실하게 머리에 그려지지 않았다. 어디에 또, 어떻게 저장되길래 변수와 자료형의 종류에 따라 다른 결과가 나오는 걸까? 실제 메모리에서 어떻게 저장되는지 또, 변수의 종류에 따라 어떤 차이가 있는지 확실하게 집고 넘어가고 싶었다.

그래서 실제 런타임 데이터 영역에서 네 종류의 변수가 어디에 생성되고, 자료형에 따라 어떻게 저장되는지 파헤져보았다.

😳JVM 구조부터 알아볼까?

먼저 열심히 작성한 소스 파일(.java)은 자바 컴파일러에 의해 클래스 파일(.class)로 컴파일 된다.

JVM이 실행되면 클래스 파일은 클래스 로더에 의해 동적으로 JVM 메모리에 로드된다. 클래스 파일에 있는 바이트 코드가 JVM 메모리와 연결되는 과정에서 클래스 파일이 명세에 맞게 구성되어 있는지 확인하고 클래스가 필요로 하는 메모리를 할당한다.

이후 런타임 데이터 영역에 있는 클래스 파일의 정보를 바탕으로 실행 엔진이 자바 바이트코드를 실행한다.

런타임 데이터 영역

런타임 데이터 영역은 크게 여섯 가지로 나뉘는데, 이 중 세 곳이 변수의 저장과 관련이 있다.

메소드 영역

JVM이 읽어 들인 각각의 클래스와 인터페이스에 대한 런타임 상수 풀, 필드와 메소드 정보, Static 변수(클래스 변수), 메소드의 바이트코드 등을 보관한다. 모든 쓰레드가 공유하는 영역으로 JVM이 시작될 때 생성된다.

동적으로 객체가 생성되며 인스턴스 변수가 이 곳에 저장된다. 모든 쓰레드가 공유하며 참조되고 있지 않은 객체는 GC(가비지 컬렉션)의 대상이 된다.

JVM 스택

각 쓰레드마다 생성되며 지역 변수매개 변수가 이곳에 저장된다. 메소드 호출 시 스택 프레임이 생성되고 내부의 지역 변수 배열에 각 변수가 저장된다.

이제 변수와 자료형의 종류에 따라 어디에, 어떻게 저장되는지 자세히 알아보자.

지역 변수의 Pass by value

public class ValuePass {
    public static void main(String[] args) {
        ValuePass valuePass = new ValuePass();
        valuePass.callPassByValue();
    }

    public void callPassByValue() {
        int localVar = 10;
        System.out.println("callPassByValue = " + localVar);  //10
        passByValue(localVar);                                //100
        System.out.println("callPassByValue = " + localVar);  //10
    }

    public void passByValue(int parameter) {
        parameter = 100;
        System.out.println("passByValue = " + parameter);
    }
}

위 코드의 결과를 보면 지역 변수를 매개 변수로 전달해 메소드를 호출한 후에도 호출한 곳의 지역 변수는 영향을 받지 않는 것을 볼 수 있다.

그 이유를 JVM 스택으로 확인해보자.

먼저 callPassByValue() 메소드가 호출되면 스택 프레임A가 생성되고 해당 스택 프레임 내부 지역 변수 배열의 1번지에 지역 변수 값 10이 저장된다.

지역 변수 배열의 0번지는 해당 메소드가 포함된 실제 인스턴스의 this 주소가 저장되며, 1번지 부터는 매개 변수가 저장되고 그 다음부터 지역 변수가 저장된다.

그 후 passByValue(int parameter) 메소드가 호출되면 스택 프레임B가 생성되고 지역 변수 배열의 1번지에 매개 변수 값 10이 저장된다. 이때 해당 메소드는 전달 받은 매개 변수 값을 100으로 변경하는데 값이 변경되는 메모리 주소가 메소드를 호출한 곳의 지역 변수가 저장된 곳과 다른 곳이기 때문에 전혀 영향을 미치지 않는다.

지역 변수의 Pass by reference

그렇다면 Pass by reference는 어떨까?

위의 ValuePass 클래스에 인스턴스 변수와 생성자를 추가하고 이를 참조하는 ReferencePass 클래스를 만들자.

public class ValuePass {int instanceVar = 0; //인스턴스 변수

    public ValuePass(int instanceVar) {
        this.instanceVar = instanceVar;
    }}

public class ReferencePass {
    public static void main(String[] args) {
        ReferencePass referencePass = new ReferencePass();
        referencePass.callPassByReference();
    }

    public void callPassByReference() {
        ValuePass reference = new ValuePass(10);
        System.out.println("callPassByReference = " + reference.instanceVar); //10
        passByReference(reference);                                           //100
        System.out.println("callPassByReference = " + reference.instanceVar); //100
    }

    public void passByReference(ValuePass parameter) {
        parameter.instanceVar = 100;
        System.out.println("passByReference = " + parameter.instanceVar);
    }
}

마찬가지로 각 메소드 호출 시 스택 프레임이 생성된다. 이번에는 지역 변수가 참조 자료형이기 때문에 스택 프레임A 내부 지역 변수 배열 1번지에는 힙에 생성된 객체의 주소가 저장되며, 매개 변수 역시 객체의 주소를 전달 받는다.

이렇게 두 변수 모두 같은 곳을 참조하게 되는데 이 방식을 Pass by reference라고 한다.

매개 변수로 전달 받은 객체의 데이터를 변경하면, 같은 객체를 참조하는 지역 변수 역시 데이터가 변경된다. 정확히 말하자면 지역 변수는 가만히 있었지만 참조하고 있던 객체의 데이터가 변해버렸다.

만약 여기서!

호출된 메소드에서 새로운 객체를 생성한 후 매개 변수에 대입하면 어떻게 될까?

아래 그림처럼 스택 프레임B의 지역 변수 배열 1번지는 새 객체의 주소로 변경되며 아래 그림처럼 서로 다른 객체를 바라보게 된다.

따라서 객체를 새로 생성하면 각 변수가 참조하는 객체가 달라지기 때문에 객체의 데이터를 변경해도 서로에게 아무런 영향을 미치지 않는다.

🧐인스턴스 변수와 클래스 변수는?

인스턴스 변수와 클래스 변수도 마찬가지다. 변수가 위치하는 메모리 영역만 다를 뿐이지 원리는 같다.

결국 매개 변수로 값을 전달하는지, 주소를 전달하는지에 따라 해당 변수가 위치한 곳에서 위와 같은 원리가 적용된다.

정리

네 가지 변수가 저장되는 위치와 자료형에 따른 저장 방식을 표로 나타내면 다음과 같다.

지역 변수매개 변수인스턴스 변수클래스 변수
저장 위치JVM 스택 내부 지역 변수 배열JVM 스택 내부 지역 변수 배열메소드 영역
기본 자료형값을 저장값을 저장값을 저장값을 저장
참조 자료형주소를 저장주소를 저장주소를 저장주소를 저장

Pass by value와 Pass by reference, 각 방식이 JVM 내부에서 어떻게 적용되는지 알아보았다.
사실 그냥 이해하고 넘어가도 되는 부분이었지만, 자세히 알고 나니 이전에는 별생각 없이 선언했던 변수들이 이젠 어떻게 흘러가는지 자연스럽게 그려진다.

참고

https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-2.html

profile
조금이라도 매일 실천하는 것을 좋아합니다.

0개의 댓글