개요
어플리케이션이 기동되기 위해서는 프로그램이 메모리에 떠야한다. 그럼 메모리 내에 어떻게 뜨는지 살펴보자
내용
운영체제에 할당받는 대표 영역
언어마다 어떻게 메모리에 영역을 할당해서 관리하는지 다르지만 자바 기준으로 대표적인 영역을 살펴보자
코드 영역
클래스 파일에 저장된 바이트코드가 로드되어 실행되는 메모리 영역.
주로 프로그램의 코드(메서드 등)를 저장하며, 불변이기 때문에 읽기 전용이다. Java에서는 JVM이 실행할 때 이 영역에 바이트코드를 저장하고 실행한다.
데이터 영역
정적 변수와 전역 변수가 저장되는 공간. Java에서는 주로 static 키워드로 선언된 변수들이 이 영역에 위치한다. 프로그램이 시작되면 할당되고 프로그램이 종료될 때까지 유지되며, 정적 메서드나 변수들이 여기에 위치한다.
힙 영역
Java에서 힙 영역은 동적 메모리 할당이 이루어지는 공간으로, 객체와 인스턴스 변수가 할당된다.
즉, Java에서 new 키워드를 통해 생성된 객체들이 힙에 저장된다. 이 영역은 가비지 컬렉터(Garbage Collector)가 관리하며, 더 이상 참조되지 않는 객체들은 이 영역에서 정리된다.
스텍 영역
스택 영역은 메서드 호출과 관련된 메모리가 저장되는 공간이다. 메서드가 호출될 때마다 스택 프레임(Stack Frame)이 생성되고, 메서드의 로컬 변수와 매개변수가 저장된다. 메서드가 종료되면 해당 스택 프레임이 사라진다. 스택은 LIFO(Last In First Out) 방식으로 관리된다.
코드로 보는 스텍 영역 예제
간단한 예제
public class Main {
public static void main(String[] args) {
int a = 7;
int b = 3;
int c = a + b;
}
}
위와 같은 코드가 있다고 했을 때 main 메소드 내의 스텍 프레임에는 아래와 같은 변수들이 저장되게 된다
심화 예제
조금 더 복잡한 코드를 보자
public class Main {
public static void main(String[] args) {
int a = 100;
a = wow(a);
}
public static int wow(int num) {
int b = num * 4;
return b;
}
}
우선 코드가 차례로 실행 되면서 메인 메소드에 대한 스텍 프레임이 생성되고 지역 변수 a에 대한 값이 스텍 프레임 내에 할당된다
이후 wow라는 메소드를 호출하면서 새로운 스텍 영역이 할당된다. 이때 지역변수 num을 받아주기 때문에 wow 메소드의 스텍 프레임에 최초로 num 변수가 할당된다.
이후에 b가 계산된 후 할당된다.
이후에 return문을 만나게 되면 wow 메소드의 스텍 메모리 영역에서 스텍 프레임은 사라지게 된다.
이렇게 스텍 프레임이 스텍 영역에 너무 많이 쌓이는 에러를 StackOverFlow라고 한다.
코드로 보는 힙 영역 예제
간단한 예제
이제 스텍 프레임을 알았으니 우리는 힙 영역까지 살펴볼 수 있다.
public class Main {
public static void main(String[] args) {
Counter c = new Counter();
}
}
public class Counter {
private int state = 0;
public void increment() {
state++;
}
public int get() {
return state;
}
}
이런 코드가 있다고 했을 때 우선 스텍 프레임이 새로 생성된다.
이후 new Counter(); 를 만난다. 그럼 내부적으로 어떤일이 일어날까?
먼저 새로운 스텍 프레임이 생성된다.
생성자도 메소드의 일종이기 때문이다. 그리고 생성자로 인해 드디어 힙에 값이 생긴다
이후 생성자라는 메소드가 끝났으므로, 생성자에 대한 스텍 프레임은 끝이 나고, 메인 메소드의 스텍 프레임에 c라는 변수에 대한 값이 생기게 된다.
이때 이전 스텍 영역의 예제와 달리, 해당 값은 힙 메모리의 값을 참조하게 된다.
심화 예제
public class Main {
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();
}
}
public class Counter {
private int state = 0;
public void increment() {
state++;
}
public int get() {
return state;
}
}
좀 더 복잡한 예제를 보자. 이제 메인 메소드가 실행되었을 때 메인 메소드에 대한 스텍 프레임이 생기는 것쯤은 우리에게 당연한 일이다.
이후에 똑같이 생성자에 대한 스텍 프레임이 생기고
생성자를 통해 힙 메모리에 값이 할당되면
생성자 메소드의 스텍 프레임이 사라지고, 메인 메소드의 스텍 프레임에 c라는 값이 힙 메모리의 주소값을 참조한다.
이제 two()라는 메소드를 호출한다. 그럼 당연히 스텍 프레임이 생길 것이다.
이때 재밌는 점은 파라미터로 우리가 만든 c를 넘겨준다는 것이다. 그런데 c는 참조형 값이므로 똑같이 샬라샬라 주소를 참조하게 된다.
그리고 c에 대하여 increase 메소드를 호출하면, 메소드이기 때문에 스텍 프레임이 새로 생기는데, 이 메소드는 Counter의 인스턴스에 대한 메소드 호출이기 때문에 내부적으로 자기 자신을 가르키는 this라는 변수가 생성된다.
이후 해당 인스턴스의, 힙 메모리의 state를 1 증가키고 스텍 프레임이 사라지며 이 과정은 한 번 더 반복된다.
한번 더 해당 과정을 반복하여 state가 2가 되었다고 하자. 그럼 two 메소드가 본인의 역할을 다 끝냈으므로 two 메소드의 스텍 프레임이 사라지게 된다.
이후 c.get이 호출되면서 get 메소드에 대한 스텍 프레임이 생성된다. 이때 또 인스턴스 내의 메소드이므로 자기 자신을 뜻하는 this가 암묵적으로 생성된다.
이후 get 메소드가 끝나고, 해당 값을 메인 메소드에서 변수로 참조 하였으므로 그 값을 메인 메소드의 스텍 프레임에 저장하고 끝이난다.
출처
https://www.youtube.com/watch?v=GIsr_r8XztQ&list=PLcXyemr8ZeoT-_8yBc_p_lVwRRqUaN8ET&index=4
'컴퓨터 공학' 카테고리의 다른 글
(코딩 컨벤션) Predicate Methods에서 'should', 'must', 'need' 는 어떻게 써야 좋을지에 대한 고찰 (0) | 2024.11.24 |
---|