변수의 기본형 & 참조형 타입
변수(variable)란 데이터(data)를 저장하기 위해 프로그램에 의해 이름을 할당받은 메모리 공간을 의미한다. 그리고 자바에서 말하는 데이터 타입(자료형)이란, 변수에 적재할 데이터가 메모리에 어떻게 저장되고 프로그램에서 어떻게 처리되어야 하는지를 명시적으로 알려주는 키워드 이다.
이 데이터 타입(자료형)은 크게 기본형 타입 과 참조형 타입으로 구분 된다.
- 기본형(primitive type)
- 정수, 실수, 문자, 논리 리터럴등의 실제 데이터 값을 저장하는 타입
- 참조형(reference type)
- 객체의 주소를 저장한다. null 또는 객체의 주소(4byte, 0x0 ~ 0xffffffff)를 갖는다.
- 객체(Object)의 번지를 참조(주소를 저장)하는 타입으로 메모리 주소 값을 통해 객체를 참조하는 타입
기본형 타입 (Primitive Type)
기본형 타입에는 크게 논리형 (boolean), 문자형 (char), 정수형 (byte, short, int, long) 실수형 (float, double) 으로 나뉜다.
기본형 타입에 대한 특징으로는 다음과 같다.
- 모두 소문자로 시작된다
- 비객체 타입이므로 null 값을 가질 수 없다. (기본값이 정해져 있음)
- 변수의 선언과 동시에 메모리 생성
- 모든 값 타입은 메모리의 스택(stack)에 저장됨
- 저장공간에 실제 자료 값을 가진다
char 연산 시 주의할 점
JVM은 연산할 때 피연산자 스택을 활용한다. 예를 들어 13 + 20 + 7 을 계산한다면, 20 + 7 을 계산한 결과를 스택에 집어 넣고, 다시 27 + 13 을 계산한 결과를 스택에 집어 넣는 방식을 반복한다. 이때 주의할 점은 JVM의 피연산자 스택은 피연산자를 4 Bytes 단위로 저장한다는 것이다. 즉, char나 short와 같이 int보다 작은 자료형의 값을 계산하면 int형으로 자동 형 변환되어 연산이 수행된다.
참조형 타입 (Reference Type)
참조형 타입은 간단히 말하자면, 위의 8가지 자료형(primitive type)을 제외한 나머지를 말한다고 보면 된다. 기본적으로 제공하는 클래스, 프로그래머가 스스로 만든 클래스, 배열, 열거 타입 등을 모두 참조형이라고 한다.
참조형 타입 특징으로는 다음과 같다.
- 기본형 과는 달리 실제 값이 저장되지 않고, 자료가 저장된 공간의 주소를 저장한다.
- 즉, 실제 값은 다른 곳에 있으며 값이 있는 주소를 가지고 있어서 나중에 그 주소를 참조해서 값을 가져온다.
- 메모리의 힙(heap)에 실제 값을 저장하고, 그 참조값(주소값)을 갖는 참조 타입 변수는 스택에 저장
- 그리고 객체를 사용할 때마다 참조 변수에 저장된 객체의 주소를 불러와 사용하게 된다.
- 참조형 변수는 null로 초기화 시킬 수 있다
정적 메모리 스택(Stack) 영역
- 스택 영역에는 기본타입 변수가 할당되고 변수의 실제 값들이 저장된다.
- 참조 타입의 변수들은 이 스택 영역에서 힙 영역에 생성된 객체들의 주소 값을 저장하고 있는다.
- 객체 안의 메소드의 작업이 종료되면 할당되었던 메모리 공간은 반환되어 비워진다.
동적 메모리 힙(Heap) 영역
- 힙 영역에는 실제 객체와 배열이 생성된다.
- 그리고 참조타입(배열, 클래스, 인터페이스 등)들이 이 객체들의 주소를 스택 영역에 저장한다.
- 기본타입 변수들과는 다르게 크기가 정해져 있지 않다.
- 프로그램 실행시 메모리에 동적으로 할당된다.
- 참조하는 변수가 없으면 자바의 GC(Garbage Collector)가 제거한다.
Boxing, Unboxing
Boxing(박싱)은 원시 타입을 참조 타입으로 변환 시키는 것을 말하고, Unboxing(언박싱)은 참조 타입을 원시 타입으로 변환 시키는 것을 말한다.
자바 1.5 이전에는 일일히 변환 과정을 거쳐주어야 했지만, 자바 1.5부터 추가된 Auto Boxing / Unboxing 기능으로 아래의 예시와 같이 명시적으로 원시타입을 참조타입으로 감싸주지 않아도 자동으로 Boxing / Unboxing 해준다.
int i = 10;
Integer integer = i;
이러한 Auto Boxing / Unboxing 기능은 메모리 누수의 원인이 될 수도 있다.
원시 타입 vs 참조 타입
1. 성능 관점
원시 타입은 스택 영역에 존재한다. 반면 참조 타입은 스택 영역에는 참조 값만 있고, 실제 값은 힙 영역에 존재한다. 참조 타입은 최소 2번 메모리 접근을 해야 하고, 일부 타입의 경우 값을 필요로 할 때 언박싱 과정(ex. Double → double, Integer → int)을 거쳐야 하므로 원시 타입과 비교해서 접근 속도가 느린 편이다.
2. 메모리 관점
원시 타입보다 참조 타입이 사용하는 메모리 양이 압도적으로 높다. 이외의 참조 타입은 최근 들어 64 비트의 JVM을 많이 사용하므로 일반적으로 64 bits(8byte)를 차지한다고 한다
3. Null 포함 가능 여부
원시 타입은 null을 담을 수 없지만, 참조 타입은 null을 담을 수 있다. 이것은 원시 타입의 경우, 값이 없으면 디폴트 값을 반환하기 때문이다. (ex. int은 0, boolean은 false)
4. 제너릭 타입에서 사용 가능 여부
원시 타입은 제네릭 타입에서 사용할 수 없지만, 참조 타입은 가능하다.
참고