-
(Java)primitive, reference 타입Java & Kotlin 2021. 8. 26. 18:28반응형
1) 서론
primitive, reference 타입은 개발을 공부했거나 컴퓨터공학과 수업을 들었다면, 당연히 알 수 있습니다. 하지만 막상 면접 때 질문을 받으니 머릿속에서 정리가 안 되는 경험을 했습니다.
"긴장을 했기 때문"이라는 좋은 핑계가 있지만, 아무리 긴장을 해도 그 순간 바로 대답하지 못했다는 것은 제대로 알지 못했기 때문이라고 생각합니다.
그래서 이번 기회에 제대로 한번 정리해보려고 합니다.
2) primitive type
먼저 늘 그렇듯 영어의 의미를 알아야 합니다. primitive는 "초기의, 원시의"라는 의미입니다. 자바 언어에서는 primitive는 사전에 정의된 예약 키워드입니다. 즉 그 말은 변수명, 메서드명 등으로 사용할 수 없습니다.
자바에서는 아래와 같이 8개의 primitive type을 정의했습니다.
- byte, short, int, long (integer)
- float, double (floating point)
- boolean (true or false)
- char (Unicode character)
이때 특이한 것은 primitive 타입은 초기화를 하지 않더라도, 컴파일러가 기본값을 넣어줍니다. 즉 반드시 초기값을 프로그래머가 입력해줘야 할 필요가 없습니다.
하지만 아래의 코드는 각각 다른 결과를 보여줍니다. 왜 그럴까요?
public class Test { static int a; // static 전역변수 public static void main(String[] args) { System.out.println(a); // 기본값 '0' 출력 } } public class Test { public static void main(String[] args) { int a; // 지역변수 System.out.println(a); // 에러! } }
- static 전역 변수: 컴파일러에 의한 초기값 할당
- 지역변수: 컴파일러가 초기값 할당 X
사실 왜 지역변수는 컴파일러가 초기값 할당하지 않는지 알 수 없습니다. 오라클 문서에서도 이유는 찾을 수 없는데요.
제가 생각하는 이유는 지역변수, 전역 변수의 문제가 생겼을 때 에러 파악의 난이도 차이라고 생각합니다.
지역변수의 경우 메서드 안에서 사용되며, 에러가 발생했을 때 사용되는 메서드만 보면 됩니다. 즉 해당 메서드의 어떤 변수가 문제 있는지만 보면 된다고 생각합니다.
하지만 전역 변수의 경우 해당 변수는 여러 메서드에 걸쳐서 사용될 수 있습니다. 즉 컴파일 시점에 어디서 에러가 발생하는지 파악하기 어렵기 때문에 컴파일러가 알아서 초기값을 넣어주는 게 아닌가 생각합니다.
여기서 말하는 "어디서 에러가 발생하는지"의 의미는 "컴파일러가 해당 변수를 읽는 시점에 해당 변수가 어디서 사용될지 알 수 없다"는 의미입니다. 그렇기 때문에 런타임 시점에 에러가 발생하는 것보다는 그냥 해당 시점에 기본값을 넣고, 에러를 발생시키지 않는 것이 좋아 보이기 때문인 것으로 혼자 추측하고 있습니다.
3) reference type
reference는 "참고, 참조"의 의미를 가지고 있습니다. 그리고 자바에서는 클래스, 인터페이스, 변수 그리고 배열을 참조 데이터로 정의하고 있습니다.
그렇다면 뭘 참조한다는 의미일까요?
이것은 primitive, reference 타입의 메모리 저장 방식에 차이가 있습니다.
primitive 타입은 메모리의 stack 영역에 실제 값을 저장합니다. 그리고 해당 변수는 값을 그대로 가지고 있습니다.
하지만 reference 타입은 실제 값을 heap 영역에 저장하고, 저장된 값을 참조하는 주소를 stack에 저장합니다. 즉 해당 변수를 호출하면 값을 가지고 있는 주소를 호출하고, 이 주소를 따라가 실제 값을 가지고 옵니다. 그렇기 때문에 "reference(참조)"라고 할 수 있습니다.
3.1) 할당된 stack은 언제 해제?
heap 영역은 사용을 다 하고 나면, GC가 해제합니다.
하지만 stack은 GC의 영역이 아닙니다. 그렇다면 이 stack은 언제 해제될까요?
기본적으로 JVM 메모리의 스레드 영역에 stack 영역이 존재합니다. 새로운 스레드가 생긴다면 stack 영역에 frame을 만듭니다. 각각의 frame은 stack 영역을 가집니다.
즉 frame안에 reference 타입의 실제 값, stack 값들이 있습니다. 그래서 stack은 "thread-safe"하다고 표현하는데요. 왜냐하면 각각의 스레드가 가지는 frame 영역은 다른 스레드와 공유하지 않기 때문입니다.
그리고 하나의 frame은 스레드가 종료됨에 따라 pop 되어, 해제됩니다.
반응형'Java & Kotlin' 카테고리의 다른 글
인터페이스의 default, static 메소드 (0) 2021.10.06 과거의 나에게...(와인 검색 서비스 회고) (0) 2021.10.02 [Lombok]@Builder과 생성자 애노테이션 (0) 2021.08.08 Lambda(람다) 표현식 적용하기 (0) 2021.06.28 Java 불변 객체 - Immutable Object (0) 2021.04.24