[JAVA] 자바에서 변수, 객체는 어떻게 저장되는 걸까?

Mando·2023년 3월 25일
0

JAVA

목록 보기
1/10

애플리케이션은 어떻게 실행되는가?

  • 애플리케이션? 일반 사용자가 사용할 수 있는 기능을 제공하는 컴퓨터가 실행할 수 있는 명령어의 집합
  • 메모리 : 실행된 애플리케이션이 상주하는 곳
  • CPU : 명령어를 실행하는 주체

즉, 애플리케이션이 메모리에 할당된 후에 CPU에 의해서 (애플리케이션)명령어가 실행된다.
그렇기에 애플리케이션을 실행하려면 우선적으로 애플리케이션이 메모리에 할당받아야 한다.

자바의 메모리 영역

지비 프로그램이 실행되면 JVM은 OS로부터 메모리를 할당받고, 그 메모리를 용도에 따라 여러 영역으로 나누어 관리한다.

JVM의 메모리 공간은 Method(static) 영역, Stack 영역, Heap 영역으로 구분된다.

메모리를 효율적으로 사용해야 하는 이유?

컴퓨터의 메모리는 사용할 수 있는 공간이 한정되어 있기 떄문에 어떻게 관리하냐에 따라서 프로그램의 성능이 달라지기 때문

따라서 같은 기능의 프로그램이라 하더라고 메모리 관리에 따라 성능 차이가 난다.

그렇기에 Java 애플리케이션에서 메모리를 효율적으로 사용하기 위해서는 메모리 구조와 특징에 대해서 알아보자.

자바 변수의 종류

자바에서 선언 위치에 따른 변수의 종류를 알아보자

public class Main{
	private static int gage = 100; //클래스 변수
	public static void main(String[]args){ //매개변수
    	Counter c = new Counter();//지역변수
        int count=c.get();
    }
}

public class Counter{
	private int state = 0;//인스턴스 변수
    public void increament(){
    	state++;
    }
    public int get(){
    	return state;
    }
}
변수명선언위치설명생성시기
클래스 변수(static 변수)클래스 영역객체를 공유하는 변수로 여러 객체에서 공통으로 사용하고 싶을 때 정의클래스가 메모리에 올라갈 때
인스턴스 변수클래스 영역개별적인 저장 공간으로 인스턴스마다 다른 값 저장 가능인스턴스가 생성될 때
지역 변수메서드 영역메서드 내에서 선언되고 메서드 실행이 끝나면 소멸되는 변수로 초깃값을 지정한 후 사용할 수 있음위치하고 있는 메서드가 수행될 떄
매개 변수메서드 영역메서드 호출 시 '전달하는 값'을 가지고 있는 인수로 선언된 곳부터 수행이 끝날 떄까지 유효하다위치하고 있는 메서드가 수행될 때

메모리 공간 구조

예시를 들기 위해서 Heap, Stack 메모리 공간을 따로 두었지만 실제로는 한 메모리에 존재하고 두 영역이 메모리에서 차는 방향이 다르다.

Method(static) Area

  1. JVM이 실행되고 Class가 로딩될 떄 생성된다.
  2. Class의 정보, Static 변수(클래스 변수), 생성자(Constructor), 메서드(Method)와 같은 것들을 저장한다.
  3. Static 영역에 있는 것은 어디서든 접근 가능
  4. JVM이 종료 시 메모리에서 해제 된다. 즉, 프로그램이 종료되기 전까진 메모리 상에 존재한다. 따라서 무분별 하게 사용될 경우 메모리 부족 현상이 발생할 수 있다.

Heap Area

  • 모든 쓰레드가 공유하는 영역으로 쓰레드가 몇 개 존재하든, 단 하나의 영역만 존재한다.
  1. new 키워드를 통해 생성된 객체(인스턴스)와 배열이 Heap 영역에 저장된다.(즉 참조형 데이터 타입)
  2. Heap 영역의 데이터를 가리키는 Reference(참조 주소)는 Stack 영역에 적재된다.
  3. Heap 영역의 참조형 데이터는 Garbage Collector가 더 이상 참조되지 않은 객체임을 확인하고 제거한다.

Stack Area

  • 메서드 호출시마다 각각의 스택 프레임(메서드만을 위한 공간)이 생성된다.
  • 스택 프레임안에 호출된 메서드의 매개변수, 지역변수, 리턴 값 및 연산 시 일어나는 값들을 임시로 저장한다.
  • 스택 프레임에서 다른 스택 프레임에 저장되어 있는 값들을 참조할 수 없다.
    마지막으로, 메서드 수행이 끝나면 프레임별로 삭제한다.

예제를 통해 알아보자!

stack 예제 1번

public class Main {
    public static void main(String[] args) {
        int a = 7;
        int b = 3;
        int c = a + b;
    }
}
  1. main 메서드를 실행하면 stack 영역에 main 메서드의 stack frame이 생성된다.
  2. 이후 main 메서드의 지역변수인 a,b,c가 main stack frame안에 저장된다
  3. main 메서드 실행이 끝나면 main stack frame 또한 메모리에서 삭제된다.![]<img

stack 예제 2번

public class Main {
    public static void main(String[] args) {
        int a = 100;
        a= wow(a);
    }

    private static int wow(int num) {
        int b = num*4;
        return b;
    }
}
  1. main 메서드를 실행하면 stack 영역에 main 메서드의 stack frame이 생성된다.
  2. main 메서드의 매개변수인 args와 지역변수인 a가 main stack frame에 저장된다.
  3. 이후 wow 메서드를 호출하면서 wow stack frame이 생성된다.
  4. wow 메서드의 매개변수인 num, 지역변수인 b가 wow stack frame에 저장된다.
  5. wow 메서드가 return을 하면서 wow 메서드의 호출은 끝나게 되고 wow stack frame은 삭제된다. 이후 wow 메서드가 return 한 값은 main stack frame 안에 있는 a 지역변수에 저장된다.
  6. main 메서드 실행이 끝나면 main stack frame 또한 메모리에서 삭제된다.

stack, heap 예제 3번

public class Main {
    public static void main(String[] args) {
        int a = 100;
        Counter counter = new Counter();
    }
}

class Counter{
    private int state=0;
    public void increment(){
        state++;
    }
    public int get(){
        return state;
    }
}
  1. main 메서드를 실행하면 stack 영역에 main 메서드의 stack frame이 생성된다.
    이후 main stack frame에 main 메서드의 매개변수인 args, 지역변수인 a가 저장된다.
  2. Counter 클래스의 생성자를 호출하였다. 생성자 역시 메서드이기 때문에 Stack 영역에counter stack frame이 생성된다.
    또한 heap 영역에 Counter 객체가 생성되고 state 가 저장된다.
    counter stack frame에는 this가 존재한다. 이 this는 counter 객체의 메모리 주소를 저장하고 있다.

    this 참조변수로 인스턴스 자신을 가리킨다. 참조변수를 통해 인스턴스의 멤버에 접근할 수 있다.

  3. 생성자가 객체를 생성한 이후 counter stack frame은 삭제되고, main stack frame에 c는 Counter 객체의 메모리 주소값을 저장한다.
  4. main 메서드 실행이 끝나면 main stack frame 또한 메모리에서 삭제된다.

stack, heap 예제 4번

    public static void main(String[] args) {
        Counter c = new Counter();
        two(c);
        int count = c.get();
    }
    public static void two(Counter c){
        c.increment();
        c.increment();
    }
}

class Counter{
    private int state=0;
    public void increment(){
        state++;
    }
    public int get(){
        return state;
    }
}
  1. main 메서드를 실행하면서 stack 영역에 main stack frame이 생긴다. 이후 Counter 생성자(즉 메서드)를 호출하면서 counter stack frame이 생긴다. 이때 this라는 참조변수가 counter stack frame이 존재하게 되는데 이 this는 Counter 객체의 주소를 가리킨다.
    Counter 객체는 heap 영역에 생성된다. 또한 상태값인 state가 저장된다.
  2. Counter 생성자가 Counter 객체를 생성한 이후 counter stack frame은 삭제되고 참조변수 c가 Counter 객체의 메모리 주소를 저장한다.
  3. 이후 two 메서드를 호출하면서 stack 영역에 two stack frame이 생성된다.
    이 two stack frame에는 매개변수인 c가 저장된다.
  4. 참조변수 c를 통해 increment를 호출한다.(즉 객체에 종속적인 메서드)
    따라서 stack 영역에 increment stack frame이 생성되고 이 frame안에 this 참조변수가 저장된다. 이 this 참조변수는 Counter 객체의 메모리 주소를 저장한다.
    이후 this를 통해 Counter 객체의 state값을 증가시킨다.
  5. increment의 호출이 끝났으므로 stack 영역의 increment stack frame은 삭제된다.
  6. 또 다시 참조변수 c에 종속적인 메서드를 호출한다. 즉 4-5번 과정을 반복한다.
  7. two 메서드의 실행이 끝났으므로 stack 영역에서 two stack frame이 삭제된다.
  8. 참조변수 c를 이용해서 객체에 종속적인 메서드를 호출하므로 stack 영역에 get stack frame이 생성되고 this 참조변수는 c 객체의 메모리 주소를 가진다.
  9. 이후 메서드 호출이 끝나면 stack 영역에서 get stack frame이 삭제된다.
  10. get 메서드를 통해 반환된 값은 main stack frame안의 count 메모리 공간에 저장된다.
  11. main 메서드 호출이 끝났으므로 stack 영역에서 main stack frame이 삭제된다.

    애플리케이션 자체가 종료되었으므로 heap 영역에 있는 Counter 객체도 삭제되는 것인가?

참고자료

쉬운코드 - 유튜브

추가로 공부할 것!

  • heap 영역에 있는 객체는 언제 삭제되는지 더 이상 참조되지 않은 객체이면 GC가 알아서 삭제하는 것으로 알고 있는데 프로그램 자체가 아예 끝나면 heap 영역에 있는 객체도 삭제가 되는 것인지

  • stack은 밑에서부터 차례대로 쌓이고, heap은 위에서부터 아래로 차례대로 쌓이는 게 맞는지 -> 지금은 heap 객체가 쌓이는 것을 stack처럼 아래서부터 그렸음

  • stack 밑에서부터 쌓이고, heap이 위에서부터 쌓인다면 그렇게 설계가 된 이유는 무엇인지?

0개의 댓글