다음 세 가지로 나눠서 JVM에게 메모리를 어떻게 할당되는지 살펴 볼 것이다.
- 클래스 변수
- 인스턴스 변수
- 지역 변수
다음 코드를 예시로 그림으로 가시화 하여 실제로 메모리가 어떻게 올라가는지 살펴보겠습니다.
class My_Obj {
Integer value;
static Integer classVar;
}
public class Main {
public static void main(String[] args) {
My_Obj obj = new My_Obj();
obj.value = 2;
obj.classVar = 1;
int a = 10;
My_Obj obj2 = new My_Obj();
obj2.value = 10;
obj2.classVar = 9;
}
}
들어가기 앞서 코드 설명을 하자면,
- obj 객체를 생성한다.
- 이 객체의 맴버 변수인 value, classVar 에 각각 2, 1를 할당한다.
- obj 객체의 맴버 변수를 출력한다. 그 다음에, obj2 객체를 생성한다.
- 그리고 이 객체의 맴버 변수인 value, classVar 에 각각 10, 11을 할당한다. 그 다음 obj2 객체의 맴버 변수를 출력한다.
그림으로 어떻게 변수들이 할당되는지 살펴보자.
크게 3가지 영역으로 나누어본다. Method, Heap, Stack 영역 으로 나뉜다. 이 중에서 클래스 변수는 클래스 파일을 읽어들이고 로더가 JVM 메모리에 올릴 때 메모리에 올라간다. 그리고 프로그램이 종료될 때 사라진다. 힙에 있는 인스턴스들은, GC의 대상이 될 때 처리가 된다. 마지막으로 지역 변수는 자신이 선언된 블록이 끝났을 때 사라진다.
처음 obj 객체의 출력 까지만 그림으로 표현해보았다. Heap 영역에는 GC가 있기 때문에 대상이 되는 인스턴스들과 Constant Pool 안에 있는 것들은 사용되지 않으면 정리가 된다. 변수에 할당된 모든 리터럴 값들은 Constant Pool 에 있고 Method 영역이나 Stack 영역에서 각 객체들은 Constant Pool 안의 해당 값의 참조 주소를 갖고 있다.
그 다음 obj2 객체가 생성이 되면 이렇게 된다.
classVar 클래스 변수가 갱신이 되면서 Constant Pool 내에 리터럴 값 9의 참조 주소를 참조하는 것으로 갱신된 것을 볼 수 있다.
그러면 My_Obj 객체의 value 필드의 타입을 참조타입에서 원시타입으로 변경 해서 살펴보자.
class My_Obj {
int value; << 원시 타입으로 변경
}
public class Main {
public static void main(String[] args) {
My_Obj obj = new My_Obj();
obj.value = 2;
int a = 11; << 새로 추가
My_Obj obj2 = new My_Obj();
obj2.value = 10;
}
}
내부 메모리상으로는 다음과 같을 것이다.
여기서 알아가야 할게 Integer는 내부적으로 캐시가 된다는 점이다.
Integer 객체는 범위 -128에서 127 사이의 값일 경우 Constant Pool에서 캐싱된 값을 사용하겠지만, 그렇지 않다면 새로운 객체가 힙에 생성됨. 증거 자료는 다음과 같다. (Integer 래퍼 클래스 내부에 정적 프라이빗 클래스인 IntegerCache와 valueOf 메소드이다.)
그러므로 예를 들어,
Integer i1 = 100;
Integer i2 = 100;
Integer i3 = 200;
Integer i4 = 200;
System.out.println(i1 == i2); // true (캐싱된 객체를 재사용)
System.out.println(i3 == i4); // false (새로운 객체가 각각 생성됨)
- i1 == i2는 true를 반환함. 왜냐하면 100은 -128 ~ 127 사이에 있으므로 캐싱된 같은 객체를 사용하기 때문.
- 반면, i3 == i4는 false를 반환함. 왜냐하면 200은 캐싱 범위 밖이므로 새로운 객체가 각각 생성되었기 때문.
그러면, 이점을 응용을 해보자. 다음 코드를 보자.
class My_Obj {
int value;
}
public class Main {
public static void main(String[] args) {
My_Obj obj = new My_Obj();
obj.value = 10;
int a = 10;
System.out.println(obj.value == a) // true 가 나온다.
}
}
이런 그림이 될 것 이다.
결론
메모리 구조를 한번 더 정리하는 계기가 되었다.
👉🏻 참조
https://codingdog.tistory.com/entry/java-클래스-변수-어떻게-메모리에-올라갈까
'언어 > 자바' 카테고리의 다른 글
List<?> 와 List 의 차이 (0) | 2023.05.03 |
---|---|
[JAVA] List<?> 와 List<Object>의 차이 (2) | 2022.09.21 |