[Kotlin] 코틀린에 primitive가 없다고? 느리지 않을까?

Kotlin|2019. 9. 10. 19:47

자바에는 primitive 타입과 Object 타입이 동시에 있는 것들이 있다는 것은

자바 개발자라면 대부분 다들 알고 있을 겁니다.

(int와 Integer , long와 Long, 등등....)

 

primitive 타입과 Object 타입이 각각 존재하는 것은 

여러 가지 이유가 있지만, 제가 생각하는 가장 큰 이유는

연산을 할 때마다 새로운 객체를 만들기 위해 메모리 할당을 한다면 성능상 문제가 생길 수 있기 때문에

(보통 Object + Object는 새로운 Object를 생성... 숫자 연산마다 메모리 할당이 생긴 하다.. 문제가 되겠죠)

출처 : https://www.scientecheasy.com/2018/06/memory-allocation-primitive-nonprimitive.html

primitive type을 두어 primitive는 heap에 할당하지 않고 스택에 value를 바로 value를 저장하여 성능 향상한 것으로 알고 있습니다.

 

primitive를 통해 성능 향상한 것은 좋지만, java Collection을 통해 int를 담거나 제네릭으로 타입을 처리하거나

할 때 primitive로는 처리가 불가능하기 때문에

primitive를 wrapping 타입(Integer)으로 변경해야 하는 경우가 필요하곤 합니다.

 

물론, java에서 자동으로 primitive에서 wrapping 타입 변환(auto-boxing) wrapping에서 primitive(auto-un-boxing)을 해주기도 하지만, 편리할 뿐 boxing 때마다 object가 생기고 사라지기 때문에 그리 성능상 좋지는 않고 불편하기도 합니다.

(NPE 문제.. 타입 처리 문제...)

 

불편하기도 하지만 성능 때문이라도 계속 primitive 타입이 존재하죠..

그래서, 많은 연산 시에는 wrapping 타입 대신 primitive 타입을 사용하라곤 합니다.

 

int[] values = {...};

int sum=0;
for(val : values){
	sum+=val;
}


Integer sum=0;//이렇게 하지 말라곤 하죠
for(val : values){
	sum+=val;//이렇게 하지 말라곤 하죠
}

 

 

그런데!

코틀린은 이 불편한 primitive가 없다고 합니다.

(실제로 코틀린은 int 대신 Int, double 대신 Double)

없어져서 좋을 수 있지만, "그럼 코틀린은 wrapping 타입만 사용한다는 걸까? 그럼 느리지 않을까?"

라는 의문이 생겼습니다.

 

그럼 위에서 언급한 연산 때마다 객체가 생기고 사라지고를 반복해서 문제가 생기지 않을까요?

 

그래서 테스트해봤습니다.

 

테스트 방법은

  • 1. 테스트 코드 작성
  • 2. class 파일 생성
  • 3. 디컴파일러로 class 파일 확인

을 통해 어떻게 class 파일이 생성되는지 확인하려고 합니다.

 

 

1. 자바

먼저, 자바로 primitive일 때

 

	int number_1 = 100;
		number_1 += 10;

	int number_2 = 200;
		number_2 += 10;

	int sum = number_1 + number_2;
		System.out.println(String.format("sum : %d", sum));

 

하단은 디컴파일러를 통해 본 자바 primitive 코드

 

거의 차이가 없습니다. 다만 마지막 라인에 String.format()에

마지막 arg는 Object []이기 때문에 Integer.valueOf()로 오토 박싱 한 부분이 보입니다.

 

지금까지 객체는 마지막 한 번만 객체 생성이 되겠네요

(물론 정확하게는 Integer.valueOf()가 내부적으로 범위 값만큼은 객체를 캐싱을 하고 있기 때문에 객체를 만들지 않겠지만요..)

 

두 번째로, 박싱 타입인 Integer로 위 코드를 사용하면 어떻게 코드가 생성되는가?

	Integer number_1 = 100;
		number_1 += 10;

	Integer number_2 = 200;
		number_2 += 10;

	Integer sum = number_1 + number_2;
		System.out.println(String.format("sum : %d", sum));

 

하단은 디컴파일러를 통해 본 자바 Integer 코드

 

 

 

100이라고 적은 int는 number_1이 Integer 타입이기 때문에 Integer.valueOf()로 오토 박싱 처리되었고

number_1 +=10 은 number_1이 Integer 타입이기 때문에 내부 primitive를 가져오는. intValue()를 사용해서 int인 10과 더하고

다시 박싱 하여 number_1에 assign 하는 식으로 동작하는 것을 볼 수 있습니다.

 

앞서 말한 것처럼 거의 연산 때마다 객체가 새로 생성되는 것을 볼 수 있습니다.

 

 

2. 코틀린

 

궁금해하던 코틀린입니다.

타입을 명시하지 않고 해 봤습니다.

(:Int로 명시해도 디컴파일 결과는 같았습니다.)

	var number_1 = 100;
		number_1 += 10;


	var number_2 = 200;
		number_2 += 10;

	var sum = number_1 + number_2;
		println("sum : ${sum}")

 

하단은 디컴파일러를 통해 본 코틀린 코드

 

 

디컴파일 했더니 박싱 타입인 Integer를 사용하지 않고 primitive 인 int로 생성되는 것을 볼 수 있었습니다.

Integer가 아닌 int로 변환한 걸 봐서는 우려했던 부분에 성능 문제는 발생하지 않겠네요

다만, println("sum : ${sum}")에 문자열 조합을

+연산을 통해 처리하고 부분이 조금 마음에 걸리네요(물론 variable이 하나라 +를 쓴 건지 나중에 확인해봐야겠네요)

 

 

마지막으로 하나 더 테스트해봤습니다.

:Int를 primitive로 처리하고 있는 건 알겠는데

"그럼 Collection에서는 primitive int로 어떻게 처리하려나?"였습니다.

 

var list = arrayListOf<Int>();

(1..10).forEach { n -> list.add(n) }

list.forEach { n -> println(n) }

 

하단은 디컴파일러를 통해 본 코틀린 코드, 실제 코드는 3줄이나, 코드는 정말 길게 만들어지네요..

 

....

 

여기서 사실 살펴볼 쪽은 색칠해둔 부분인데 너무 작아 보이지 않으실 테니

적자면

list.add(Integer.valueOf(n))

으로 처리를 하고 있습니다.

결국에는 ArrayList를 사용하기 위해 자바와 동일하게 오토 박싱을 하고 있긴 하네요

 

 

 

결론

코틀린에는 primitiveType은 없지만, 바이트코드로 변환 시 가능한 한 primitiveType으로 바꿔주어

연산 시 성능 문제는 발생하지 않을 것으로 보이며

Collection은 마찬가지로 autoBoxing 처리를 하고 있다.

 

 

나중에 자바에서는 String append시 StringBuilder나 Buffer를 사용하도록 하는데

코틀린에서는 어떻게 처리하고 있나 봐야겠습니다.

 

'Kotlin' 카테고리의 다른 글

split과 동시에 assign  (0) 2021.11.29
코틀린 upgrade 1.3.7 to 1.5.0  (0) 2021.05.28
[Kotlin] default argument, named argument  (0) 2019.09.18
[Kotlin] data class  (0) 2019.09.09
[Spring][Kotlin] 필드에 @Autowired 어떻게 사용하나?  (0) 2019.09.06

댓글()