이 글은 Effective Java 를 완독하고, Kotlin 을 상용으로 사용하는 개발자 입장에서
Effective Kotlin 글 중 새로운 내용, remind 할 필요 있는 부분, 핵심 내용 등만 추려 정리한 내용입니다.
#
동일 내용의 string literal 과 값이 작은 Boxed primitive 는 동일 JVM 안에서 재사용된다.
(Integer cache 는 -128~127 의 숫자를 들고 있다.)
#
Int? (nullable) primitive type 은 null 을 받아들여야 하기 때문에 Integer 로 사용되고, Int 의 경우 primitive int 가 사용된다.
Is object creation expensive?
#
Objects 는 추가 공간을 점유한다.
64-bit JDK 에서 12-byte header 와 8byte 배수를 맞추기 위한 패딩이 들어간다. 그래서 최소 사이즈가 16byte 이다.
32-bit JVM 에는 최소 사이즈가 8byte 이다.
object reference 자체도 공간을 차지한다. refernece 는 4~8byte 를 사용한다. (환경에 따라 다르다.)
그 자체의 비용은 작지만 그것들이 모이면 큰 비용이 된다.
primitive Int 는 4byte 지만 Integer 는 16byte 를 사용한다.
#
encapsulated 된 경우 function call access 비용이 발생한다.
function 이 빠르다면 작은 비용이지만, 반복 호출될 경우 역시나 큰 비용이 된다.
그렇다고 encapsulation 을 제한하는게 답이 아니다. 불필요한 object 생성을 막는 방향으로 가야 한다.
#
Objects 가 생성되어야 한다.
create, allocate memory, reference 가 되어야 한다.
이 과정 역시 비용이 적지만, 누적되면 큰 비용이 된다.
Object declaration
#
object 재사용의 가장 간단한 방법은 singleton 을 사용하는 것이다.
#
Nothing 은 모든 type 의 subtype 이다.
#
Immutable object 를 만들면 재사용이 또 쉬워진다.
Factory function with a cache
#
Cache 에는 WeakReference 보다 SoftReference 가 선호된다.
WeakReference 는 참조가 없어지면 바로 GC 대상이 되지만, SoftReference 는 참조가 없으면서 Memory 가 부족한 상황에서 GC 대상이 된다.
Heavy object lifting
#
fun <T: Comparable> Iterable.countMax(): Int{
val max = this.max()
return count { it == max }
// return count { it == this.max() }
}
Lazy initialization
#
Lazy 도 양날의 검이라는 것 알고 사용할 것.
Using primitives
#
nullable type 사용 & generic 에 사용하는 경우 primitive 를 사용할 수 없음.
#
억지로 primitive 로 바꾸면서 가독성이 안 좋아지는 케이스가 많아지기 때문에,
퍼포먼스 이슈가 있어서 최적화가 필요할 때만 하는 것이 권장된다.
Summary
#
안정적인 최적화 방법들이 있다.
그러나 어설픈 최적화는 오히려 가독성을 해치고, 이상한 동작을 할 수도 있으니 잘 점검하자.
끝
댓글