객체 생성은 언제나 비용이 들어가고 상황에 따라 굉장히 큰 비용이 들어갈 수 있다. 따라서 불필요한 객체 생성을 피하는 것이 최적화 측면에서 좋다.
JVM에서는 동일한 문자열을 처리한다면 기존의 문자열을 재사용한다. (리터럴을 저장하는 경우 heap영역에 있는 string constant pool에 저장하여 재사용)
var str1:String = "Lorem" // literal
var str2:String = "Lorem" // literal
var str3:String = StringBuilder("Lorem").toString() // new
println(str1 == str2) // true, 같은 주소
println(str1 === str2) // true, 같은 값
println(str2 === str3) // false
println(str2 === str3.intern()) // true, string pool에 값이 이미 있는지 찾고 해당 리터럴 값을 반환
Integer와 Long처럼 박스화한 기본 자료형도 작은 경우에는 재사용된다. (-128~127 범위를 캐시)
- 2^7 인 이유? 가장 많이 사용하는 범위이기 때문. 그래서 memory reference cached된다.
val i1:Int? = 1
val i2:Int? = 1
println(i1 == i2) // true
println(i1 === i2) // true, i2를 IntegerCache로부터 읽어 들이기 떄문
val i3:Int? = 1234
val i4:Int? = 1234
println(i3 == i4) // true
println(i3 === i4) // false, 캐싱되지 않음
객체 wrapping 시 발생하는 비용
객체 선언
inline 한정자의 역할
inline 한정자를 사용하면 함수 본문으로 점프했다가 함수를 호출했던 위치로 다시 점프하는 과정이 일어나지 않는다.
inline modifier 사용 장점
1. 타입 아규먼트에 reified 한정자를 붙여 사용 가능
- JVM 바이트 코드에는 제네릭이 존재하지 않는다. 컴파일을 하면 제네릭 타입과 관련된 내용이 제거된다. 타입 제한을 주기 위해 타입 파라미터를 사용하면 컴파일 시 타입 파라미터를 사용한 부분이 타입 아규먼트로 대체되게 된다.
2. 함수 타입 파라미터를 가진 함수가 훨씬 빠르게 동작
- 모든 함수는 inline을 붙이면 조금 더 빠르게 동작한다. 함수 호출과 리턴을 위해 점프하는 과정과 백스택을 추정하는 과정이 일어나지 않기 때문이다.
- 함수파라미터 컴파일 (매개변수 개수에 따라 functionN 타입으로 변경됨)
- ()->Unit은 Fuction0
- (Int)->Int는 Fuction1<Int, Int>
- (Int, Int)->Int는 Fuction2<Int, Int, Int>
3. 비지역적 리턴을 사용할 수 있다.
- inline을 사용하지 않은 함수의 경우에는 내부에서 return을 사용할 수 없다.
- 이는 함수가 객체로 wrapping되어서 발생하는 문제로, 함수가 다른 클래스에 위치(non-local)하기에 return을 사용해서 main 측으로 돌아올 수 없게 되는 것이다.
- inline 함수는 main 함수 내부에 박히기에 돌아올 수 있다.
non inline function인 경우 @ scope 지정해주어야 return이 가능하다.
함수를 인라인으로 만들고 싶지만, 어떤 이유로 일부 함수 타입 파라미터는 inline으로 받고 싶지 않은 경우가 있다면? crossinline, noinline 한정자 사용
비즈니스 로직을 위한 wrapper class 생성이 필요하다. 그러나 추가 heap 할당으로 인해 런타임 오버헤드가 발생하고, wrapping된 타입이 원시타입인 경우 성능이 크게 저하된다.
이러한 문제를 해결하기 위해 코틀린에서는 inline class를 제공한다.
inline class는 값 기반 클래스들의 부분집합으로 identity를 갖지 못하고, 값만 가지게 된다.
inline class
기능 및 사용법
다른 자료형을 wrapping해서 새로운 자료형을 만들 때 많이 사용한다.
JVM의 GC가 메모리 관리를 해준다고 메모리 관리를 완전히 무시한다면, 메모리 누수가 발생할 수 있다. 상황에 따라 OOM가 발생할 수 있다.
객체에 대한 참조를 companion으로 유지하면 GC가 해당 객체에 대한 메모리 해제를 할 수 없다.
따라서 의존 관계를 정적으로 저장하지 말고 다른 방법을 활용하는 것이 좋다.
객체에 대한 레퍼런스를 다른 곳에 저장할 때는 메모리 누수가 발생할 가능성을 언제나 신경써야 한다.
Array
안쓰면 null로 바꿔주어라
사용하지 않는 객체를 Null로 해라 -> gc 수거 대상이 됨
SoftReference & WeakReference & StrongReference