목차
- JVM(Java Virtual Machine) 실행 데이터 영역
- 애플리케이션이 어떻게 실행되는가?
- Stack 메모리
- Heap 메모리
- CPU는 어떻게 스택 메모리에서 데이터를 찾을까?
- 메서드 호출과 this
- 쓰레기 객체(Garabage Object)
JVM(Java Virtual Machine) 실행 데이터 영역
메서드 영역(Method Area)
- 클래스 메타데이터, 상수, 정적 변수 저장
- 모든 스레드가 공유
- 클래스 로더(Class Loader)에 의해서 로드된다.
힙(Heap)
- 모든 객체와 배열이 할당되는 메모리 영역
- 모든 스레드가 공유
스택(Stack)
- 각 스레드 마다 독립적인 스택을 가진다.
- 메서드 호출 시 생성되는 스택 프레임이 여기에 저장
- 각 스택 프레임은 지역 변수, 매개 변수, 리턴 주소, 이전 스택 프레임의 BP(BasePointer)를 저장
PC 레지스터(Program Counter Register)
- 현재 실행 중인 명령어의 주소를 가리킨다.
- 각 스레드 마다 하나씩 존재
네이티브 메서드 스택(Native Method Stack)
- 자바 외의 언어로 작성된 메서드가 실행될 때 사용
애플리케이션이 어떻게 실행되는가?
- 애플리케이션 : 일반 사용자가 사용할 기능을 제공하는, 컴퓨터가 실행할 수 있는 명령어들의 집합
- 메모리 : 실행된 애플리케이션이 상주하는 곳
- CPU : 명령어를 실행하는 주체
- runtume : 애플리케이션이 메모리에 올라가서 실행되는 시간을 의미(실행 상태)
public class Main {
public static void main(String[] args) {
int a = 7; // 1
int b = 3; // 2
int c = a + b; // 3
}
}
- cpu 메모리에 a = 7 저장
- cpu 메모리에 b = 3 저장
- cpu 메모리에서 a, b를 가져와 a + b를 연산하고 해당 값을 메모리 c에 저장
Stack 메모리
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;
}
}
| |
| |
|------------| <- main method 스택 프레임
| 100 | a
|------------|
stack
wow 함수 실행
|------------| <- wow method 스택 프레임
| return주소 | <- main 메서드의 a 주소를 가진다.
|------------|
| 400 | <-b
|------------| |
| 100 | <-num
|------------| <- main method 스택 프레임
| 100 | <- a
|------------|
wow 함수 종료
| |
| |
|------------| <- main method 스택 프레임
| 400 | a
|------------|
CPU는 어떻게 스택 메모리에서 데이터를 찾을까?
먼저 함수(메서드)를 호출을 하면 스택 프레임이 생성된다.
스택 프레임 구성
- 매개변수 : 함수에 전달된 인자들
- 로컬변수 : 함수 내에서 선언된 함수들
- 리턴주소 : 함수 호출이 끝난 후 돌아갈 주소
- 저장된 프레임 포인터(베이스 포인터) : 이전 스택 프레임의 시작 주소를 가리킨다.
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;
}
}
1. main 함수 호출
- main 함수가 호출되면, 스택에 main 함수의 스택 프레임이 생성된다.
- main 함수의 스택 프레임에는 args 와 a가 저장
2. wow 함수 호출
- main 함수에서 wow(a)가 호출될 때 새로운 프레임 생성
- 이 프레임에는 매개변수 num값과 로컬 변수 b가 저장
cpu는 스택 포인터(Stack Pointer, SP), 베이스 포인터(Base Pointer, BP)라는 레지스터를 사용하여 스택 메모리를 관리
스택 포인터
- 현재 스택의 최상단을 가리키는 레지스터
- 함수 호출 시 새로운 스택 프레임이 추가되면, 스택 포인터가 그 위치로 이동
- 함수가 종료되면, 스택 포임터가 이전의 위치로 돌아간다.
베이스 포인터
- 현재 함수의 스택 프레임의 시작 지점을 가리킨다.
- 로컬 변수나 매개변수에 접근할 때, 베이스 포인터를 기준으로 오프셋을 계산하여 접근한다.
| ... |
|------------| <--- SP
| 로컬 변수 | <- `main` 함수의 로컬 변수 (예: a)
| 매개변수 | <- `main` 함수의 매개변수 (예: args)
|------------| <--- BP (main 함수의 시작 지점)
| ... |
|------------| <--- SP
| 로컬 변수 b| <- `wow` 함수의 로컬 변수
| 매개변수 num| <- `wow` 함수의 매개변수
| 이전 BP | <- `main` 함수의 BP
|------------| <--- BP (wow 함수의 시작 지점)
| 로컬 변수 | <- `main` 함수의 로컬 변수
| 매개변수 | <- `main` 함수의 매개변수
|------------| <--- BP (main 함수의 시작 지점)
| ... |
|------------| <--- SP
| 로컬 변수 | <- `main` 함수의 로컬 변수
| 매개변수 | <- `main` 함수의 매개변수
|------------| <--- BP (main 함수의 시작 지점)
Heap 메모리
public class Main {
public static void main(String[] args) {
Counter c = new Counter();
}
}
class Counter{
private int state = 0;
public void increment(){ state++; }
public int get() {return state; }
}
main 스택 프레임 생성
new Counter() 실행 - 새로운 스택 프레임
class Counter{
//Counter(){}
private int state = 0;
public void increment(){ state++; }
public int get() {return state; }
}
Counter 생성자가 없으니까 컴파일 시점에 자동으로 매개변수가 없는 기본 생성자가 생성 해당 메서드에는 지역변수, 매개변수가 없으니까 스택 프레임에도 해당 변수들이 없지만 만약 매개변수가 있다면 이 변수들도 스택 프레임에 추가된다.
ex)
public class Main {
public static void main(String[] args) {
Counter c = new Counter(10);
}
}
class Counter{
Counter(int state){
this.state = state;
}
private int state = 0;
public void increment(){ state++; }
public int get() {return state; }
}
메서드 호출과 this
자바에서 인스턴스 메서드를 호출하면, JVM은 메서드 호출을 다음과 같은 방식으로 처리한다.
1. 객체 식별
- 메서드 호출이 발생하면 해당 객체의 레퍼런스가 메서드에 암묵적으로 전달됩니다. 이 레퍼런스가 바로 this 이다.
- 예를 들어, c.increment() 를 호출할 때, c 객체의 레퍼런스가 increment 메서드로 전달된다.
2. 스택 프레임 생성
- 메서드 호출 시 새로운 스택 프레임 생성
- 이 스택 프레임에는 메서드의 지역 변수와 매개변수들이 저장
- 인스턴스 메서드의 첫 번째 매개변수는 항상 this로, 호출된 객체의 래퍼런스를 가리킨다.
3. 메서드 실행
- 메서드 내에서 인스턴스 변수나 다른 메서드에 접근할 때, this를 사용하여 호출된 객체의 멤버에 접근한다.
- 예를 들어 state++는 실제로 this.state++
예제 코드
public class Main {
public static void main(String[] args) {
Counter c = new Counter();
c.increment();
}
}
class Counter {
private int state = 0;
public void increment() {
state++; // 실제로는 this.state++
}
public int get() {
return state; // 실제로는 this.state
}
}
결론
자바에서는 인스턴스 메서드를 호출할 때, 해당 객체의 레퍼런스가 암묵적으로 this로 전달
이를 통해 메서드는 객체의 인스턴스 변수를 식별하고 조작할 수 있다. 따라서 매개변수로 명시적으로 객체를 전달하지 않아도, 메서드 내부에서 객체를 식별할 수 있습니다.
쓰레기 객체(Garabage Object)
public class Main {
public static void main(String[] args) {
Counter c = make();
}
public static Counter make(){
Counter c = new Counter(); // 쓰레기 객체
return new Counter();
}
}
class Counter{
private int state = 0;
public void increment(){ state++; }
public int get() {return state; }
}
해당 객체는 더이상 접근할 방법(래퍼런스)이 없다
이런 접근 불가능한 객체를 쓰레기 객체라고 한다.
→ GC(garbage collector)가 해결한다.
GC의 동장 방식에 따라 애플리케이션의 성능이 크게 바뀐다.
'Computer Sience > Java' 카테고리의 다른 글
[JAVA8] 메소드 래퍼런스 (0) | 2024.09.30 |
---|---|
[JAVA8] 람다 표현식 + 변수 캡쳐 & 변수 쉐도잉 (0) | 2024.09.30 |
[JAVA8] 자바에서 제공하는 함수형 인터페이스 (0) | 2024.09.30 |
[JAVA8] 함수형 인터페이스와 람다 표현식 + 자바 함수형 프로그래밍 (2) | 2024.09.30 |
& VS && (0) | 2024.06.04 |