ye._.veloper

[ Java ] Call by Value와 Call by Reference 차이, 예제 본문

Java

[ Java ] Call by Value와 Call by Reference 차이, 예제

ye._.veloper 2023. 3. 3. 13:00

Call by Value를 공부하기 전 JVM의 메모리 영역에 대한 이해가 우선이라 하여, JVM의 Runtime Data Area에 대해서 짤막하게 정리하겠다.

 

Runtime Data Area는 JVM의 메모리 영역으로 Java Application을 실행할 때 사용되는 데이터를 적재하는 영역이다.

JVM의 메모리 구조

 

◽ Stack Area

  · 메서드 안에서 사용되는 값을 저장하고, 호출되는 메서드의 매개변수, 지역변수, 리턴 값, 연산 시 일어나는 값을 임시로 저장하는 공간

  · 원시타입(Primitive Type)의 실제 값만을 저장하는 공간

 

◽ Heap Area

  · 인스턴스와 배열이 동적으로 생성되는 공간

  · Garbage Collection의 대상이 되는 영역

  · 원시타입을 제외한 모든 데이터(= Reference Type)는 Heap 영역에 저장

  · static이 붙지 않은 것은 모두 Heap 영역에 저장 (static이 붙은 것은 static 메모리 영역에 저장됨)

 


☁ Call by Value와  Call by Reference 란?

  · 변수나 객체등이 함수의 인자(argument)로 들어와 매개변수(parameter)로 전달될 때 어떤 방식으로 전달될 지를 결정하는 방식

  · 값을 복사하여 처리하느냐 아니면 직접 참조하느냐의 차이

 

 

☁ Call by Value (값에 의한 호출)

  · 인자로 받은 값을 복사하여 처리하는 방식

  · Call by Value에 의해 넘어온 값을 증가시켜도 원래의 값이 보존된다.

  · 값을 복사하여 넘기기 때문에 메모리 사용량이 늘어난다.

 

위의 설명만으로 이해가 되지 않아 아래 예시를 통해 이해했다.

public class CallbyValue {
    public static void main(String[] args){
        int a = 10
        int b = 20;
        System.out.println("호출 전 a = " + a + ", b = " + b);

        run(a, b);
        System.out.println("호출 후 a = " + a + ", b = " + b);
    }
    
    public static void run(int x, int y) {
        x = 30;
        y = 40;
    }
}

위 코드의 결과는 아래와 같다.

호출 전 a = 10, b = 20
호출 후 a = 10, b = 20

main() 메서드에서 run() 메서드에 보내는 매개변수(x, y)의 값을 10과 20으로 주었고,

run() 메서드 실행 후, 매개변수(a, b)의 값이 30과 40으로 바뀌어서 나올 줄 알았지만 결과를 보았을 때, 값이 바뀌지 않았다.

 

 

main() 메서드에서 변수 a와 b를 전달하지만 run() 메서드 내의 두 변수 x와 y는 a, b의 두 '값'만을 복사하여 x, y에 저장한다.

결국 a와 b의 값을 직접적으로 바꾸지 못하고 새로운 변수 x, y를 만들어 저장하는 것이다.

 

 

위 코드 실행 결과로 알게된 점은 Call by Value는 파라미터로 전달받은 변수의 값을 복사하여 새로운 변수를 생성해 저장한다는 의미로 이해할 수 있었다.

 


 

☁ Call by Reference (참조에 의한 호출)

  · 인자로 받은 값의 주소를 참조하여 직접 값에 영향을 주는 방식

  · 값을 복사하지 않고 직접 참조하기 때문에 속도가 빠르다.

  · 원래의 값에 영향을 주는 리스크가 존재한다.

 

CallbyReference 의미 이해를 돕기 위해 아래와 같은 예제를 만들었다.

public class CallbyReference {

    static class Test{
        int num;
    }

    public static void main(String[] args) {
        Test test = new Test();
        test.num = 10;
        System.out.println("호출 전 num = " + test.num);

        change(test);
    }

    private static void change(Test test){
        test.num = 100;
        System.out.println("호출 후 num = " + test.num);
    }
    
}

main() 메서드에서 Test 클래스에 대한 객체를 생성하였고, 그 객체의 변수에 숫자 '10'을 저장했다.

change() 메서드 실행 전과 후의 결과 값을 비교해보겠다.

위 코드의 결과는 아래와 같다.

호출 전 num = 10
호출 후 num = 100

CallbyValue와는 다른 결과가 나왔다.


위 예제를 통해 볼 수 있듯이 Java에서 객체를 전달받고, 그 객체를 수정하면 원래 있던 값도 수정되므로 CallbyReference처럼 보이지만, Java는 기본적으로 모든 전달 방식이 CallbyValue이다.

CallbyReference로 작동하는 것처럼 보일 뿐 CallbyValue로 작동한다.

 

CallbyReference는 참조하는 변수가 그 변수의 주소 값을 갖고 그 변수의 주소 값으로 해당 값을 찾는 방식이다.

파라미터를 넘기는 과정에서 직접적인 참조를 넘긴 것이 아닌, 주소 값을 복사해서 넘긴 것이기 때문에 이는 CallbyValue이다.

복사된 주소값으로 참조가 가능하니 주소 값이 가리키는 객체의 내용이 변경되는 것이다.

 

위 그림과 같이 Java에서는 똑같은 값이 있는 경우, 메모리 낭비를 줄이기 위해 그 값을 가리키는 형태로 메모리를 사용한다.

Java는 C언어와 다르게 메모리 자체에 접근을 할 수 없기 때문이다.

 

 

 

 

Ref.

[Java] Call by value vs Call by reference

Call by reference 와 Call by Value 차이

'Call by value'와 'Call by reference' (feet. 자바스크립트)

[ Call By Value와 Call By Reference 차이 ]

 

 

 

 

 

 

Comments