[Java] 원시 타입(Primitive Type) vs 참조 타입(Reference Type)

🌈 m1naworld ·2022년 11월 9일
0

자바를 잡아! 👻

목록 보기
6/19
post-thumbnail

자바에서는 데이터 타입이 크게 두 가지 원시 타입(Primitive Type)참조 타입(Reference Type)이 있다.

원시 타입정수, 실수, 문자, 논리 리터럴을 저장하는 타입을 말하며, 참조 타입은 객체의 번지(주소)를 참조하는 타입으로 배열, 열거, 클래스, 인터페이스를 말한다.

원시 타입(Primitive Type)

참조 타입(Reference Type)

참조 타입은 원시 타입을 제외한 타입들(문자열, 배열, 열거, 클래스, 인터페이스)을 말함


💡 원시 타입과 참조 타입 차이

원시 타입으로 선언된 변수와 참조 타입으로 선언된 변수의 차이점은 저장되는 값이다. 원시타입의 변수는 실제 값을 변수 안에 저장하지만, 참조 타입인 변수는 메모리의 번지를 변수 안에 저장하여 저장된 메모리 번지 값을 통해 객체를 참조한다.


예시)

아래와 같이 선언된 원시 타입과 참조타입이 있을 경우

// 원시 타입 변수 
int age = 25;
double price = 100.5;

// 참조 타입 변수
String name = "송민아";
String hobby = "코딩";

메모리에서 이 변수들이 갖는 값을 그림으로 표현하면 다음과 같다.

Java에서 실제 객체는 힙(heap) 영역에 저장되며, 참조 타입 변수는 스택 영역에 실제 객체들의 주소를 저장하여, 객체를 사용할 때마다 참조 변수에 저장된 객체의 주소를 불러와 사용하는 방식이다.


❗️원시 타입과 참조 타입의 사용 측면으로의 차이는 크게 3가지가 있음!

1. Null

원시 타입은 null을 담을 수 없으나, 참조 타입은 null을 입력값으로 받을 수 있음.

int i = null; // 불가능
Integer integer = null; // 가능

2. 제네릭 타입

원시 타입은 제네릭 타입에서 사용할 수 없으나, 참조 타입은 제네릭 타입에서 사용할 수 있음.

List<int> i; // 불가능
List<Integer> integer; // 가능

3. 접근 속도, 메모리 양

원시 타입은 null을 다루지도 못하고, 제네릭에 담기지도 못함. 그러나 원시 타입이 참조 타입과 비교해서 갖는 장점은 '성능'상의 이점이 있음.

  • 접근 속도
    원시 타입은 '스택' 메모리에 값이 존재한다. 반면에 참조 타입은 하나의 인스턴스이기 때문에 '스택'메모리에는 참조값만 있고, 실제 값은 힙 메모리에 존재한다. 그리고 값을 필요로 할 때마다 *언박싱 과정을 거쳐야 하니 원시 타입과 비교해서 접근 속도가 느려진다.
    (예외적으로 엄청 큰 숫자를 복사해야 한다면, 참조값만 넘길 수 있는 참조 타입이 좋을 수도 있음)

  • 차지하는 메모리 양
    차지하는 메모리에 양도 참조타입이 훨씬 많음.

추천: 성능과 메모리에 장점이 있는 원시 타입을 먼저 고려해 본다. 만약 Null을 다뤄야 하거나, 제네릭 타입에서 사용되어야 한다면 참조 타입을 사용한다.



*Boxing, Unboxing

Boxing(박싱)원시 타입 ➡️ 참조 타입으로 변환 시키는 것을 말하고, Unboxing(언박싱)참조 타입 ➡️ 원시 타입으로 변환 시키는 것을 말함

좀 더 자세히 설명하자면은, Boxing은 값 타입을 참조 타입으로 변환하여 값을 포함하는 객체를 힙에 생성하는 것이며 Unboxing은 Boxing된 참조 타입으로부터 원래의 값을 다시 추출하는 연산을 뜻한다.

자바 1.5 이전에는 일일히 변환 과정을 거쳐주어야 했지만, 자바 1.5부터 추가된 Auto Boxing/Unboxing기능으로 아래의 예시와 같이 명시적으로 원시타입을 참조타입으로 감싸주지 않아도 자동으로 Boxing/Unboxing 해준다.

int i = 15;
Integer integer = i;

그러나, 이러한 Auto Boxing/Unboxing기능은 컴파일러 내부적으로 추가 연산작업을 거치게 되어 메모리 누수의 원인이 될 수도 있다. 따라서 성능 향상을 위해서 Auto Boxing/Unboxing이 일어나지 않도록 동일한 타입 연산이 이루어지도록 구현하는 것이 좋다.

Auto Boxing 연산

public static void main(String[] args) {
    long t = System.currentTimeMillis();
    Long sum = 0L;
    for (long i = 0; i < 1000000; i++) {
        sum += i;
    }
    System.out.println("실행 시간: " + (System.currentTimeMillis() - t) + " ms");
}

// 실행 시간 : 19 ms

동일 타입 연산

public static void main(String[] args) {
    long t = System.currentTimeMillis();
    long sum = 0L;
    for (long i = 0; i < 1000000; i++) {
        sum += i;
    }
    System.out.println("실행 시간: " + (System.currentTimeMillis() - t) + " ms") ;
}

// 실행 시간 : 4 ms

100만건 기준으로 약 5배의 성능 차이가 난다고 한다. 불필요한 Auto Boxing/Unboxing은 줄일 필요가 있다!



Ref.
책: 혼자 공부하는 자바
gil.log
siyoon210
데브타임즈
Jay Jang

profile
개발자로 사는 내 삶은 즐거워 👾

0개의 댓글