메모리 할당
- 어떤 변수 선언 = 메모리 할당
 - 변수를 선언하기 위해 할당되는 메모리로는 크게 스택과 힙이 있음
 
스택(Stack) 영역
- 함수의 호출과 함께 지역 변수 / 매개 변수 등이 할당됨
 - 정렬된 방식으로 메모리 할당 및 해제됨
 
힙(Heap) 영역
- 클래스 변수나 인스턴스 변수 / 객체 등이 할당됨
 - 우연하고 무질서하게 메모리 할당됨
 - ➡️ JVM 은 무질서하게 관리되는 힙 영역을 위주로, GC 통해 메모리 해제 관리함
 - (인스턴스 변수로 존재하는 원시 변수는 힙 영역에서 관리됨)
 
원시 변수(Primitive Value)의 메모리 할당
- 자바에서 변수는 객체가 아닌 실제 값들인 int, double, float boolean 등과 같은 원시 값(Primitive Value)들이 존재
 
public void test() {
    // Primitive Value
    int x = 3;
    float y = 10.012f;
    boolean isTrue = true;
}
- 메모리에서는 아래처럼 스택 영역에 할당됨
 - 스택 영역에 실제 값들이 저장되는 것
 

객체(Object)의 메모리 할당
- 원시 변수는 스택 영역에 실제 값들이 할당됨
 - 그러나 객체는 원시 변수와 다른 방식으로 값이 할당됨
 
public void test() {
    // Primitive Value
    int x = 3;
    float y = 101.012f;
    boolean isTrue = true;
    
    // Object
    String name = "MangKyu";
    
    String[] names = new String[3];
    names[0] = "I";
    names[1] = "am";
    names[2] = new String("MangKyu");   
}
- 먼저 객체의 경우 힙 영역에 실제 값이 할당됨
 - 그리고 이에 접근하기 위해 스택 영역에는 힙 영역에 존재하는 값의 실제 주소가 저장됨
 - (C/C++에서는 이를 포인터(pointer)라 부름)
 - 즉, 스택 영역에는 실제 값이 존재하는 힙 영역의 주소가 저장됨
 

- 힙 영역은 스택 영역과 다르게, 무질서하게 메모리 공간을 활용함
 - 객체의 메모리 할당인 경우, 스택 영역에 실제 값을 참조하기 위한 Reference(참조값)이 저장됨 ➡️ 이를 참조하여 실제 값에 접근
 - 배열의 경우, 그 배열의 인덱스마다 참조값이 할당되며, 이를 통해 실제 값에 접근
 
테스트 코드
class Dog {
    private String name;
    public Dog (String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
public class Test {
    public static void main(String[] args) {
        int x = 10;
        int[] y = {2, 3, 4};
        Dog dog1 = new Dog("강아지1");
        Dog dog2 = new Dog("강아지2");
        
        // 함수 실행
        foo(x, y, dog1, dog2);
        
        System.out.println("x = " + x);
        System.out.println("y = " + y[0]);
        System.out.println("dog1.name = " + dog1.getName());
        System.out.println("dog2.name = " + dog2.getName());
    }
    public static void foo(int x, int[] y, Dog dog1, Dog dog2) {
        x++;
        y[0]++;
        dog1 = new Dog("이름 바뀐 강아지1");
        dog2.setName("이름 바뀐 강아지2");
    }
}
테스트 결과
x = 10
y = 3
dog1.name = 강아지1
dog2.name = 이름 바뀐 강아지2
- 위 결과의 근본적인 이유 : 자바는 Pass By Value 로 변수를 전달하기 때문
 
foo() ➡️ int x
- 스택에 파라미터가 할당됨 = 즉, 새로운 x 가 스택에 10의 값으로 생성됨
 - foo 는 x 값 증가시킴 = 기존의 x 가 아닌, 새로운 x 값을 증가시킴
 - ➡️ 기존의 x 는 10 으로 값 유지
 - ➡️ 새로운 x 에 10 할당된 후, 11 로 증가
 - ➡️ foo 함수 종료 시, 새로운 x 는 스택에서 해제됨
 - 그러므로 foo 호출한 후 x 출력 시, 기존의 x 출력됨
 

foo() ➡️ int y
- 스택에 새로운 파라미터인 int[] y 할당
 

- y 의 0 번째 인덱스에 존재하는 값을 1 증가
 - 새로운 y 가 할당되었으나, x 와 달리 배열의 주소를 가리킴
 - 해당 y 가 기존의 y 이든 새로운 y 이든, 실제 배열의 주소로 접근하여 값을 1 증가시키기에, foo가 종료된 후에도 배열의 값은 변화하게 됨
 

foo() ➡️ Dog dog1
- 스택에 새로운 파라미터인 dog1 생성
 - y 와 마찬가지로 dog1 은 객체이므로 dog1 의 주소를 값으로 가짐
 

- foo 함수는 dog1 을 새로 생성
 - ➡️ 힙에는 새로운 dog1 이 생성됨
 - ➡️ 스택에서 가리키는 주소값은 새로운 dog1 을 가리키게 됨
 - ➡️ foo 함수 종료 시, 새로운 dog1 은 소멸 및 기존의 dog1 에는 변화 X
 

foo() ➡️ Dog dog2
- 네 번째 파라미터로 Dog 클래스의 인스턴스인 dog2 를 받아옴
 - dog1 때와 마찬가지로 스택에는 새로운 dog2 할당됨 ➡️ 실제 dog2 의 주소값 가리킴
 

- 그러나 dog2 가 수행하는 연산 != dog1 연산
 - dog2 는 새로운 값 할당 X, 주소값 통해 힙에 존재하는 객체에 접근 ➡️ set() 으로 값 변화시킴
 

- foo() 함수 종료 시, 스택에 할당된 새로운 dog2 소멸됨
 - 그러나 힙에 존재하는 값은 변하였기에 foo 함수가 종료된 후 dog2 의 name 출력 시 "이름 바뀐 강아지2"임을 확인
 
foo() 종료 결과

Pass by Value (값에 의한 전달)
- 복사된 데이터를 전달하여 구성
 - 값을 수정해도 원본 데이터에는 영향 주지 않음
 - process() 내에서 해당 값을 수정해도, 해당 해당 함수가 종료되면 원본 값은 기존 상태 유지
 

- 먼저 main() 호출 ➡️ 스택에 main() 과 그 지역 변수로 선언한 someValue 존재
 - 그 위로 process() 의 Return Address 가 스택에 쌓임
 - 그 위로 process() 의 파라미터인 int 변수 value 가 스택에 쌓임
 
- 메모리에 할당된 main 함수의 someValue는 그대로 유지된 상태
 - 7이라는 값만 복사하여 새롭게 메모리를 할당함
 - main() 의 someValue 주소값 = 0x07040
 - process() 의 value 주소값 = 0x07000
 - 두 변수는 값만 같을뿐 다른 존재
 
Q. process() 로 전달받은 파라미터 value 에 어떤 연산이 수행된 후 process() 가 종료되면?
- process() 위해 할당된 Call Stack 이 pop 되고 ➡️ 해당 메모리는 모두 소멸
 - 그렇기에 process() 종료된 후 main() 에서 someValue 를 다시 출력해도 7이라는 기존 값 유지

https://mangkyu.tistory.com/107  
Pass by Reference (참조에 의한 전달)
- Pass by Reference 가 어떤 Alias 를 구성하여 실제 값에 접근한다는 관점에서
 - Pass by Pointer 는 Alias 로 주소값을 넘겨주었을 뿐이기 때문에 동일한 기술이라고 볼 수 있음
 - Pass By Reference 는 주소값을 전달해 실제 값에 대한 Alias 를 구성
 - ➡️ 값 수정 시, 원본 데이터가 수정되게 함
 - Pass by Value 에서는 실제 값이 쌓인 것과 다르게, Pass by Reference 에서는 실제 값의 주소가 쌓임
 

- process() 에서 파라미터로 전달받은 value 는 someValue 가 저장된 값을 참조하는 Alias
 - value 를 10 으로 변경 = value가 가리키는 메모리(실제 someValue 가 저장된 메모리)의 값을 7에서 10으로 변경
 - 그래서 process() 종료 후 someValue 출력 시, 10 으로 변경되어 있음
 
자바에서 Pass by Value
- 이론적으로 원시 값은 스택 메모리에 실제 값이 저장되고, 객체는 실제 메모리를 참고하기 위한 값이 저장됨
 - 그렇기에 자바에서는 객체에 한해 확장된 규칙이 적용됨
 - 객체 복사 후 전달되는 값 = 실제 메모리를 가리키는 Reference(참조값)인 포인터
 
자바에서 객체 생성 시
Dog dog = new Dog();
- dog 은 실제로 생성된 Dog 클래스의 객체를 저장하는 것이 아니고,
 - Dog 클래스의 객체가 저장된 주소값(포인터값)을 가지고 있는 것
 - 자바에서의 객체 전달 = 이런 dog 변수가 복제되어 전달
 - 자바에서 어떤 객체가 파라미터로 전달되면, 필드값에 접근해 해당 값 수정은 가능하나, 그 객체 자체는 변경 불가능
 
예시
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);
    }
}

- 이번에는 main() 에서 Foo 클래스에 대한 객체 생성 후 이를 process() 의 파라미터로 넘김
 - 자바에서는 Pass by Value 에 따라 Foo 객체가 존재하는 주소(0x07070)를 갖는 someFoo 변수를 복사하여 전달
 - ➡️ process() 가 주소값을 통해 필드값에 접근하게 해줌
 - 그러나 process() 종료 시, copied pointer(0x07000) 는 소멸됨
 - Foo 객체 자체에 변경사항이 있었다면 이는 반영되지 않음
 
Pass by Reference(참조에 의한 전달)
- 주소값을 전달하여 실제 값에 대한 Alias 를 구성 = 값 수정 시 원본의 데이터가 수정되는 방식
 - 자바에서 객체를 전달하는 방식에는 분명 주소값이 전달되고 있지만, 이는 someFoo 에 대한 복사본일 뿐
 - process() 의 파라미터로 전달받은 foo 역시 실제 주소값을 참조하고 있기에, foo 의 setValue 를 통해 실제 객체의 필드값 수정 시 결과에 반영됨
 - 그러나 이는 객체의 주소값으로 객체의 필드값에 접근하여 값을 변경하는 것일 뿐, 실제 객체 자체에 변화 X
 
정리
| 전달 방식 | Pass by Value | Pass by Reference | 
| 의미 | 복사된 데이터를 전달해 구성하는 방식 | 주소값을 전달해 실제 값에 대한 Alias 구성하는 방식 | 
| 원본 데이터 변화 | 값을 수정해도 원본 데이터는 변화 X | 값 수정 시, 원본 데이터 수정됨 | 
출처
https://mangkyu.tistory.com/105
'Backend > Java' 카테고리의 다른 글
| [Java] Exception (0) | 2023.12.05 | 
|---|---|
| [Java] Enum (0) | 2023.12.05 | 
| [Java] 기본 자료형 & 참조 자료형 (0) | 2023.12.05 | 
| [자바의 신] 29장 (ongoing) (0) | 2022.08.31 | 
| [자바의 신] 28장 (ongoing) (0) | 2022.08.31 | 
										
									
										
									
										
									
										
									
댓글