JVM(Java Virtual Machine, 자바 가상 머신)은 Java의 Bytecode를 해석하고 실행하는 역할을 한다.
- JVM만 설치하면 운영체제(OS)에 관계 없이 java파일을 실행시킬 수 있다.
변수는 크게 네 종류로, 변수의 선언된 위치에 따라 클래스변수
, 인스턴스변수
, 지역변수
, 매개변수로
나뉜다.
public class Variable {
public static int age = 20; // 클래스 변수 (전역 변수)
int height = 60; // 인스턴스 변수 (전역 변수)
public static void main(String[] args) { // 매개변수 (파라미터)
int size = 50; // 지역 변수
}ㅍ
}
클래스 변수(Static 변수), 생성자(Constructor), 메소드(Method), Class의 정보 등을 저장하는 공간
Class
가 로딩될 때 생성된다.메소드 내에서 정의하는 기본 자료형(primitive Type)에 해당하는 지역변수의 데이터 값이 저장되는 공간
• int, double, byte, long, boolean 등
스택 프레임
이 생기고, 그 안에 메소드를 호출❓ 스택 프레임(stack frame)
: 하나의 메서드에 필요한 메모리 덩어리를 묶어서 스택 프레임(Stack Frame)이라고 한다.
메서드의 매개변수
, 지역 변수
, 리턴값
등이 있다.인스턴스(객체), 배열 등 참조형(Reference Type) 데이터 객체의 실제 데이터가 저장되는 공간이다.
• String, array, enum, class, interface, Object 등
new 키워드
로 인스턴스를 생성할 때 Heap 영역에는 생성된 객체가 저장되고, Stack 영역에는 생성된 객체에 대한 주소 값(Reference)이 저장된다.
→ Heap 영역에 있는 데이터를 가리키는 레퍼런스 변수는 Stack에 저장된다.
```java
public class Main {
public static int s = 10;
public static void main(String[] args) {
int a = 5;
int b = 5;
int result1 = a + b + Main.s;
System.out.println(result1); // 20
Counter sub = new Counter();
twice(sub);
int result2 = sub.get();
System.out.println(result2); // 100
}
public static void twice(Counter c) {
c.plus(10);
c.plus(20);
}
}
class Counter {
public int state = 50;
public final int count = 20;
public int get() {
return state + count;
}
public void plus(int n) {
state += n;
}
}
```
클래스 변수(static)와 메소드(method)는 무조건 Method 영역에 적재된다.
💡 이때 일반 인스턴스 변수인 Counter 클래스의 변수
state
와count
는 final 키워드가 붙었지만, 클래스 변수(static)나 메소드(method)가 아니므로 메서드 영역에 들어가지 않는다.
Class Main의 메인 메서드(public static void main(String[] agrs
)가 실행되면 스택 영역에 스택 프레임이 쌓이고 지역 변수와 매개 변수가 담긴다.
생성자 new Counter()
를 호출하면 Heap 영역에 Counter 클래스 인스턴스 변수들이 저장된다.
그리고 Stack 영역의 지역변수 sub
에 주소값으로 연결되게 된다.
twice(sub)
메소드를 실행하면 Stack 영역에 새로운 스택 프레임이 쌓인다.
Arguments
로 클래스를 전달했기 때문에 twice()
의 매개 변수 c
는 주소값으로 같은 힙 영역을 가리키게 된다.
plus()
메소드를 호출해 실행하면 → 메소드이기 때문에 Stack 영역에 새로운 스택 프레임이 생성된다.this
라는 암묵적인 변수가 자동 생성되게 되는데, 이 this
변수는 자동으로 Heap 영역에 있는 Counter 객체를 가리키게 된다.plus()
메소드 안의 코드 state += n
가 동작하면서 Heap 영역에 있는 인스턴스 변수 state
의 값이 변하게 된다.❶ 실행 후 종료된 plus()
스택 프레임은 Stack 영역에서 제거된다.
❷ 그 후 sub
객체 변수의 메소드인 get()
을 호출하면 Stack 영역에 새로운 스택 프레임이 생기고, this
변수가 Heap 영역의 객체를 가리키게 된다. 그리고 Heap 영역의 변수를 반환한다.
❸ 실행이 종료된 get()
스택 프레임이 Stack 영역에서 제거되고, main 스택 프레임에 result2
지역 변수가 추가된다.
💡 TIP!
호출되는 메서드가 파라미터로 객체값을 전달받아 객체의 상태를 변경하게 되면, 메서드 종료(스택 제거) 이후에도 Heap 영역에 있는 객체의 상태는 쭉 유지된다.
Stack 영역은 메서드의 끝을 알려주는 닫는 중괄호( }
)를 만나면 자동으로 메모리에서 제거된다.
❗️그러나 Heap 영역에는 여전히 객체 데이터가 메모리에 남아있다.
코드 실행이 모두 끝나면 Method(Static)영역이 비워진다.
❗️ 가비지 컬렉터(GC)는 Heap 영역에 남아있는 더 이상 참조되지 않는 객체를 식별해 청소하는 역할을 한다.
변수 name은 Stack 영역에 저장되며, 해당 변수의 주소는 Heap 영역에 저장되어 ‘Mark’를 가리킨다.
힙 메모리는 애플리케이션의 모든 부분에서 사용되며, 반면에 스택 메모리는 하나의 스레드가 실행될 때 사용
→ 따라서 힙 과 메서드 공간에 저장된 객체는 어디서든지 접근이 가능하지만, 스택 메모리는 다른 스레드가 접근할 수 없다.
언제든지 객체가 생성되면 항상 힙 공간에 저장되며, 스택 메모리는 힙 공간에 있는 객체를 참조만 한다.
→ 즉, 스택 메모리는 primitive 타입의 지역변수와 힙 공간에 있는 객체 참조 변수만 갖고 있다.
스택메모리의 생명주기는 매우 짧으며, 힙 메모리는 애플리케이션의 시작부터 끝까지 살아남는다.
자바 코드를 실행할때 따로 -Xms
과 -Xmx
옵션을 사용하면 힙 메모리의 초기 사이즈와 최대 사이즈를 조절할 수 있다.
스택 메모리가 가득차면 자바에서는 java.lang.StackOverFlowError
를 발생하고, 힙 메모리가 가득차면 java.lang.OutOfMemoryError : Java Heap Space
에러를 발생
스택 메모리 사이즈는 힙 메모리와 비교했을 때 매우 적다. 하지만 스택 메모리는 간단한 메모리 할당 방법(LIFO)를 사용하므로 힙 메모리보다 빠르다.