[기본 개념] 1 | (2.4) JVM 메모리, 기본형/참조형 매개변수

728x90

[기본 개념] 1 | (2.4) JVM 메모리, 기본형/참조형 매개변수

1 선언 위치에 따른 변수의 종류

2 클래스변수와 인스턴스변수

3 메서드

4 메서드의 선언과 구현

5 메서드의 호출

6 return문

7> JVM의 메모리구조

8> 기본형 매개변수와 참조형 매개변수

9 참조형 반환타입

10 재귀호출

11 클래스 메서드와 인스턴스 메서드

12 클래스 멤버와 인스턴스 멤버간의 호출

7. JVM의 메모리 구조

 응용프로그램이 실행되면, JVM은 시스템으로부터 프로그램을 수행하는데 필요한 메모리를 할당받고 JVM은 이 메모리를 용도에 따라 여러 영역으로 나누어 관리한다. 그중 3가지 주요 영역(method area, call stack, heap)에 대해 알아보자.

 

Method Area
클래스 데이터(cv)    클래스 데이터
Call Stack
main(lv)
Heap
인스턴스(iv)    인스턴스    인스턴스

 

1. 메서드 영역(method area)

- 프로그램 실행 중 어떤 클래스가 사용되면, JVM은 해당 클래스의 클래스파일을 읽어서 분석하여 클래스에 대한 정보(클래스 데이터)를 이곳에 저장한다. 이때, 그 클래스의 클래스변수(class variable)도 이 영역에 함께 생성된다.

 

2. 힙(heap)

- 인스턴스가 생성되는 공간. 프로그램 실행 중 생성되는 인스턴스는 모두 이곳에 생성된다. 즉, 인스턴스변수(instance variable)들이 생성되는 공간이다.

 

3. 호출스택(call stack 또는 execution stack)

- 메서드의 작업에 필요한 메모리 공간을 제공한다. 메서드가 호출되면, 호출스택에 호출된 메서드를 위한 메모리가 할당되며, 이 메모리는 메서드가 작업을 수행하는 동안 지역변수들과 연산의 중간결과 등을 저장하는 데 사용된다. 그리고 메서드가 작업을 마치면 할당되었던 메모리 공간은 반환되어 비워진다.

 

 각 메서드를 위한 메모리 상의 작업공간은 서로 구별되며, 첫 번째로 호출된 메서드를 위한 작업공간이 호출스택의 맨 밑에 마련되고, 첫 번째 메서드 수행 중에 다른 메서드를 호출하면, 첫 번째 메서드의 바로 위에 두 번째로 호출된 메서드를 위한 공간이 마련된다.

 

 이 때, 첫 번째 메서드는 수행을 멈추고, 두 번째 메서드가 수행되기 시작한다. 두 번째로 호출된 메서드가 수행을 마치게 되면, 두 번째 메서드를 위해 제공되었던 호출스택의 메모리공간이 반환되며, 첫 번째 메서드는 다시 수행을 계속하게 된다. 첫 번째 메서드가 수행을 마치면, 역시 제공되었던 메모리 공간이 호출스택에서 제거되며 호출스택은 완전히 비워지게 된다.

 

따라서 호출스택의 특징을 정리해보면 다음과 같다.

 

- 메서드가 호출되면 수행에 필요한 만큼의 메모리를 스택에 할당받는다.

- 메서드가 수행을 마치고 나면 사용했던 메모리를 반환하고 스택에서 제거된다.

- 호출스택의 제일 위에 있는 메서드가 현재 실행 중인 메서드이다.

- 아래에 있는 메서드가 바로 위의 메서드를 호출한 메서드이다.

 

 반환타입(return type)이 있는 메서드는 종료되면서 결과값을 자신을 호출한 메서드(caller)에게 반환한다. 대기상태에 있던 호출한 메서드(caller)는 넘겨받은 반환값으로 수행을 계속 진행하게 된다.

 

예제/CallStackTest.java

public class CallStackTest {
    public static void main(String[] args) {
        fristMethod();  // static메서드는 객체 생성없이 호출가능하다.
    }
    static void fristMethod() {
        secondMethod();
    }
    static void secondMethod() {
        System.out.println("secondMethod()");
    }
}

 

 main( )이 firstMethod( )를 호출하고 firstMehtod( )는 secondMethod( )를 호출한다. 객체를 생성하지 않고도 메서드를 호출할 수 있으려면, 메서드 앞에 'static'을 붙여야 한다.

 

 위 예제를 실행시켰을 때, 프로그램이 수행되는 동안 호출스택의 변화를 그림과 함께 살펴보도록 하자.

 

        println        
secondMethod secondMethod secondMethod
firstMethod firstMethod firstMestod firstMestod firstMestod
main main main main main main main
1 2 3 4 5 6 7 8 9

 

1 ~ 2 위의 예제를 실행시키면, JVM에 의해 main메서드가 호출됨으로써 프로그램이 시작된다. 이때, 호출스택에는 main메서드를 위한 메모리공간이 할당되고 main메서드의 코드가 수행된다.

3 main메서드에서 firstMethod( )를 호출한 상태이다. main메서드가 끝난 것이 아니므로 main메서드는 호출스택에 대기 상태로 남아있고 firstMethod( )의 수행이 시작된다.

4 firstMethod( )에서 다시 secondMethod( )를 호출했다. firstMethod( )는 secondMehtod( )가 수행을 마칠 때까지 대기상태에 있게 된다. secondMethod( )가 수행을 마쳐야 firstMethod( )의 나머지 문장들을 수행할 수 있기 때문이다.

5 secondMethod( )에서 println( )을 호출했다. println메서드에 의해 'secondMethod( )'가 화면에 출력된다.

6 println메서드의 수행이 완료되어 호출스택에서 사라지고 자신을 호출한 secondMethod( )로 되돌아간다. 대기 중이던 secondMethod( )는 println( )을 호출한 이후부터 수행을 재개한다.

7 secondMethod( )에 더 이상 수행할 코드가 없으므로 종료되고, 자신을 호출한 firstMethod( )로 돌아간다.

8 firstMethod( )에도 더 이상 수행할 코드가 없으므로 종료되고, 자신을 호출한 main메서드로 돌아간다.

9 main메서드에도 더 이상 수행할 코드가 없으므로 종료되어, 호출스택은 완전히 비워지게 되고 프로그램은 종료된다.

8. 기본형 매개변수와 참조형 매개변수

 자바에서는 메서드를 호출할 때 매개변수로 지정한 값을 메서드의 매개변수에 복사해서 넘겨준다. 매개변수의 타입이 기본형(primitive type)일 때는 기본형 값이 복사되지만, 참조형(reference type)이면 인스턴스의 주소가 복사된다.

 

 기본형 매개변수  변수의 값을 읽기만 할 수 있다.

 참조형 매개변수  변수의 값을 읽고 변경할 수 있다.

 

예제/PrimitiveParamEx.java

class Data { int x; }

public class PrimitiveParamEx {
    public static void main(String[] args) {
        Data d = new Data();
        d.x = 10;
        System.out.println("main() : x = " + d.x);

        change(d.x);
        System.out.println("After change(d.x)");
        System.out.println("main() : x = " + d.x);
    }

    static void change(int x) {  // 기본형 매개변수
        x = 1000;
        System.out.println("change() : x = " + x);
    }
}
실행결과

main() : x = 10
change() : x = 1000
After change(d.x)
main() : x = 10

 

1. change메서드가 호출되면서 'd.x'가 change메서드의 매개변수 x에 복사됨

2. change메서드에서 x의 값을 1000으로 변경

3. change메서드가 종료되면서 매개변수 x는 스택에서 제거됨

 

 'd.x'의 값이 변경된 것이 아니라, change메서드의 매개변수 x의 값이 변경된 것이다. 즉, 기본형 매개변수는 변수에 저장된 값만 읽을 수만 있을 뿐 변경할 수 없다.

 

예제/ReferenceParamEx.java

class Data { int x; }

public class ReferenceParamEx {
    public static void main(String[] args) {
        Data d = new Data();
        d.x = 10;
        System.out.println("main() : x = " + d.x);

        change(d);
        System.out.println("After change(d.x)");
        System.out.println("main() : x = " + d.x);
    }

    static void change(Data d) {  // 참조형 매개변수
        d.x = 1000;
        System.out.println("change() : x = " + d.x);
    }
}
실행결과

main() : x = 10
change() : x = 1000
After change(d.x)
main() : x = 1000

 

1. change메서드가 호출되면서 참조변수 d의 값(주소)이 매개변수 d에 복사됨. 이제 매개변수 d에 저장된 주소 값으로

   x에 접근 가능

2. change메서드에서 매개변수 d로 x의 값을 1000으로 변경

3. change메서드가 종료되면서 매개변수 d는 스택에서 제거됨

 

 매개변수를 참조형으로 선언했기 때문에, x의 값이 아닌 주소가 매개변수 d에 복사되었다. 이제 main메서드의 참조변수 d와 change메서드의 참조변수 d는 같은 객체를 가리키게 된다. 그래서 매개변수 d로 x의 값을 읽는 것과 변경하는 것이 가능한 것이다.

 

   
    int
add (int a, int b) {

        return a + b ;
    }

<--->
    void
add (int a, int b, int[ ] result) {

        result[0] = a+ b ;
    }

 

 반환값이 있는 메서드를 반환값이 없는 메서드로 바꾸는 방법을 보여준다. 참조형 매개변수를 활용하면 반환값이 없어도 메서드의 실행결과를 얻어 올 수 있다. 이것을 응용하면 여러 개의 값을 반환받는 것과 같은 효과를 얻을 수 있다.

 

 

 

 

출처 | Java의 정석 (남궁 성)

728x90