JAVA

[JAVA] Call by Value & Call by Reference

날아 2023. 2. 5. 16:41

1. Call by Value

값에 의한 호출

  • 함수가 호출될 때, 메모리 공간 안에서는 함수를 위한 별도의 임시공간이 생성된다.(종료 시 사라짐)
  • call by value 호출 방식은 함수 호출 시 전달되는 변수 값을 복사해서 함수 인자로 전달
  • 이때 복사된 인사는 함수 안에서 지역적으로 사용되기 때문에 지역변수 속성을 가진다.
  • 따라서 함수 안에서 인자 값이 변경되더라도 외부 변수 값은 변경 안됨 

 

동작 방식

프로그래머가 어떤 함수를 호출한다고 하면 Stack 메모리에 먼저 함수의 Return Address가 쌓이고 그 위에 매개변수 등의 값이 쌓이게 된다.

  • main 함수를 호출하였기 때문에 main 함수의 내용이 Stack에 먼저 존재할 것이고, 그 중에서 지역 변수로 선언한 someValue가 존재할 것이다. 이후에 process라는 함수를 호출하는 상황이라고 한다면, 먼저 Return Address가 Stack에 쌓이고, 그 다음으로 process의 파라미터인 int형 변수 value 역시 쌓이게 된다.
  • 여기서 주목해야 할 부분은 메모리에 할당된 main 함수의 someValue는 그대로 유지된 상태로, 7이라는 값만을 참고하고 복사하여 새롭게 메모리를 할당한다는 점이다. 실제로 main함수의 someValue의 주소값은 0x07040 이지만, process함수의 파라미터인 value의 주소값은 0x07000 이라는 점에서 두 변수는 값만 같을뿐 다른 존재임을 확인할 수 있다. 
  • process 함수로 전달받은 파라미터 value에 어떤 값이 더해지고 빼지고 곱해진 상태로 process 함수가 종료된다면 process를 위해 할당된 Call Stack이 pop될 것이고 해당 메모리는 모두 소멸될 것이다.

 

  • 그렇기 때문에 process가 종료된 이후 main 함수에서 someValue를 다시 출력하여 보아도 7이라는 기존의 값을 유지하는 것이다. 그리고 이렇게 어떤 함수를 호출할 때 파라미터로 값을 복사하여 전달하는 방식을 Call By Value라고 한다. 

2. Call by Reference 

참조에 의한 호출

  • call by reference 호출 방식은 함수 호출 시 인자로 전달되는 변수의 레퍼런스를 전달한다.
  • 따라서 함수 안에서 인자 값이 변경되면, 아규먼트로 전달된 객체의 값도 변경된다.

 

동작방식

Stack 메모리에 먼저 process 함수의 Return Address가 쌓이게 되고 그 위에 매개변수 등의 값이 쌓이게 된다. 하지만 Pass By Value에서는 실제 값이 쌓였던 것과 다르게 Pass By Reference에서는 실제 값의 주소가 쌓이게 된다.

  • process 함수에서 파라미터로 전달받은 value는 someValue가 저장된 값을 참조하는 Alias이며 value를 10으로 변경하는 것은 value가 가리키는 메모리(실제 someValue가 저장되어 있는 메모리)의 값을 7에서 10으로 변경하는 것이다.
  • 따라서 process 함수 종료 후에 someValue를 출력하여 확인해보면 10으로 변경이 된 것을 확인할 수 있다.

3. JAVA에서 Pass By Value의 동작방식 

  • JAVA는 모든 데이터를 Pass by Value 방식으로 전달한다. 
  • 다만, 객체에 한해서 확장된 규칙이 적용되는데, 객체와 관련되어 복사 후 전달되는 값은 실제 메모리를 가리키는 Reference인 포인터라는 것이다. 
  • Java에서 객체를 생성할 때 Dog dog = new Dog()과 같은 표현을 사용한다. 여기서 dog은 실제로 생성된 Dog 클래스의 객체를 저장하고 있는 것이 아니고, Dog 클래스의 객체가 저장된 주소값(포인터 값)을 가지고 있는 것이다. 그리고 Java에서 객체를 전달한다고 하면 이러한 dog 변수가 복제되어 전달된다. 
  • 따라서 Java에서 어떤 객체가 파라미터로 전달되었을 때, 필드값에 접근하여 해당 값을 수정하는 것은 가능하지만 그 객체 자체는 변경 불가능한 것이다. 

  기존의 예시와는 다르게 이번에는 main 함수에서 Foo 클래스에 대한 객체를 생성하고, 이를 process 함수의 파라미터로 넘기고 있다. 앞서 설명한대로 Java에서는 Pass By Value에 따라 Foo 객체가 존재하는 주소(0x07070)를 갖는 someFoo 변수를 복사하여 전달함으로써, process의 함수가 주소값을 통해 필드값에 접근하는 것을 도와주고 있다. 하지만 process가 종료되면 copied pointer(0x07000)는 소멸되고, foo 객체 자체에 변경사항이 있었다면 해당 부분 역시 반영되지 않는다.

 

  분명 누군가는 Java의 객체 전달 방식이 Pass By Reference가 아닌가 혼란이 올 수 있다. 하지만 앞서 정의되었던 Pass By Reference의 정의를 살펴보면 이를 해결할 수 있다. Pass By Reference(참조에 의한 전달)는 주소 값을 전달하여 실제 값에 대한 Alias를 구성함으로써, 값을 수정하면 원본의 데이터가 수정되도록 하는 방식이라고 정의하였다. Java에서 객체를 전달하는 방식에는 분명 주소값이 전달되고 있지만, 이는 그저 someFoo에 대한 복사본일 뿐이다. 물론 process의 파라미터로 전달받은 foo 역시도 실제 주소값을 참조하고 있기 때문에 foo의 setValue를 통해 실제 객체의 필드값을 수정하면 변하게 된다. 하지만 이는 그저 객체의 주소값으로 객체의 필드 값에 접근하여 값을 변경하는 것일 뿐, 실제 객체 자체에 변화를 주는 것이 아니다.

 

class Foo {

    private int value;

    public Foo(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }

}

public class Main {

    public static void main(String[] args) {
        Foo someFoo = new Foo(7);
        process(someFoo);
        System.out.println(someFoo.getValue());
    }

    public static void process(Foo foo) {
        // 주소 값을 통해 필드 값을 변경
        foo.setValue(10);

        // 객체 자체를 변경하는 것은 영향 X
        foo = new Foo(15);
    }

}

 

  위의 process 함수에서는 파라미터로 전달받은 foo 객체를 15라는 값을 지닌 새로운 객체로 변경하고 있다. process 함수가 종료되면 어떻게 되겠는가? 당연히 10이라는 값을 유지하게 된다. 그러한 이유는 foo는 Pass By Reference처럼 실제 객체에 대한 Alias가 아니라, 그저 someValue가 복사되어 전달된 Reference이기 때문이며, Java가 Pass By Value 방식으로 동작하기 때문이다. Java가 Pass By Reference 방식으로 동작하였다면 process 함수가 종료된 후에도 15라는 값으로 유지되었을 것이다. 

 

 

 

출저

'JAVA' 카테고리의 다른 글

[JAVA] Thread 란  (0) 2023.02.06
[JAVA] GC 동작 알고리즘  (0) 2023.02.05
[JAVA] 예외처리와 올바른 예외처리 방법  (0) 2023.01.29
[JAVA] Object 클래스 - equals와 hashCode 함수  (0) 2023.01.29
[JAVA] static에 대하여  (0) 2023.01.29