독서

[이펙티브 코틀린] 8장. 효율적인 컬렉션 처리

오렌지색 귤 2024. 8. 11. 21:40
반응형

아이템 49. 하나 이상의 처리 단계를 가진 경우에는 시퀀스를 사용하라

 

p. 361

 

Q. 자바 8의 스트림을 사용하면 코틀린의 시퀀스와 마찬가지로 지연 처리되는가?

 

 

코틀린의 시퀀스와 자바 8의 스트림은 둘 다 지연 연산을 통해 요소를 처리한다는 점에서 비슷합니다.


지연 연산을 사용하면 필요할 때마다 요소를 계산하며, 불필요한 계산이나 메모리 사용을 줄일 수 있습니다

 

 

 

 

p. 362

 

 

Think Twice Before Using Java 8 Parallel Streams - DZone

Parallelization was the main driving force behind lambdas, stream API, and others. Let's take a look at why you should think twice before using this feature.

dzone.com

 

자바 스트림에서 병렬함수의 결함

 

자바 8 스트림 api의 병렬 처리를 사용해야 하는 상황

  • cpu 집약적인 계산이 필요한 경우
  • 느린 네트워크 작업이 필요한 경우

 

오류 상황

 

코드를 실행할 때마다 다른 결과가 얻어지거나, 가끔은 몇 가지의 작업이 느린 작업 뒤에 멈춰있기도 한다.

 

 

해결 방법

  1. common join-fork 풀에 제출된 모든 작업이 중단되지 않고 적절한 시간 내에 완료되도록 보장
  2. 병렬 스트림에 사용할 스레드 풀을 지정할 수 있는 자바 버전 업데이트

 

 

아이템 50. 컬렉션 처리 단계 수를 제한하라

 

p. 365


Q. 연산 내용이 시퀀스 객체로 전달되므로, 인라인으로 사용할 수 없습니다. 따라서 람다 표현식을 객체로 만들어 사용해야 합니다. 의 의미

 

 

람다 표현식과 객체화

 

람다 표현식은 코틀린에서 익명 함수로, 간단하게 정의할 수 있는 방법을 제공합니다.


하지만 코틀린의 람다 표현식은 JVM에서 실행될 때 함수형 인터페이스(Functional Interface)로 변환됩니다.


이 과정에서 람다 표현식은 객체로 변환되어 사용됩니다.

 

 

 

객체화 과정

 

  1. 람다 표현식 정의

람다 표현식을 정의하면, 이는 JVM에서 익명 클래스로 컴파일되어 객체로 생성됩니다.
예를 들어, list.map { it * 2 }와 같은 람다 표현식이 있을 때, 이 표현식은 내부적으로 익명 클래스로 변환됩니다.

 

 

  1. 함수형 인터페이스

JVM에서 함수형 인터페이스는 하나의 추상 메소드를 가지는 인터페이스로 정의됩니다.
코틀린의 람다 표현식은 이러한 함수형 인터페이스의 구현체로 변환되어 실행됩니다.

 

 

  1. 인라인 불가

람다 표현식을 직접 인라인으로 사용하는 대신, 객체 형태로 생성되기 때문에 함수 호출과 객체 생성에 따른 오버헤드가 발생합니다.
이 과정에서 JVM은 람다 표현식을 객체로 감싸서 처리하므로, 실제로 람다 표현식이 인라인으로 실행되는 것은 아닙니다.

 

 

 

인라인 함수와 차이점

 

인라인 함수 안에서 람다 표현식을 사용하면, 이 람다도 인라인될 수 있어 추가적인 객체 생성 없이 직접 호출된다.

 

 

시퀀스에서 인라인이 안되는 이유

 

설계 목적이 다르다.

  • 시퀀스는 연산을 지연하고 중간 결과를 관리하는 특성을 갖고 있기 때문에, 연산 체이닝에서의 람다 표현식은 객체로 존재해야 한다.
  • 인라인 함수는 성능 최적화를 위한 것이지만, 시퀀스의 경우 인라인이 설계 목적에 맞지 않아 적용되지 않는다.

 

 

 

아이템 51. 성능이 중요한 부분에는 기본 자료형 배열을 사용하라

 

 

p. 368

 

IntArray와 List<Int>의 바이트 수가 5배 정도 차이가 발생하는 이유

 

 IntArray

 

메모리 구조

 

  • IntArray는 JVM의 원시 타입 배열이라, 배열의 각 요소는 고정 크기인 4바이트의 정수형으로 직접 저장
  • 따라서 1,000,000개의 정수를 저장하는 경우 1,000,000 * 4 = 4,000,000 바이트의 메모리를 사용

 

추가 오버헤드

 

  • JVM에서 배열은 객체로 관리되므로, 배열 자체의 메타데이터를 위한 약간의 오버헤드가 존재
  • 이러한 오버헤드는 대략 16바이트 정도로, 배열의 크기에 비해 무시할 수 있을 정도로 작다

 

List<Int>

 

메모리 구조

 

  • List<Int>는 보통 ArrayList로 구현되며, 내부적으로 객체 배열 (Object[])을 사용
  • 각 Int는 객체 형태의 Integer로 저장되며, Integer 객체는 객체 포인터와 원시 타입 데이터를 포함한다

 

추가 오버헤드

 

  • 각 Integer 객체는 16바이트의 오버헤드를 가진다 (8바이트의 객체 헤더 + 4바이트의 int 값 + 4바이트의 정렬 패딩)
  • 따라서 1,000,000개의 Integer 객체는 1,000,000 * 16 = 16,000,000 바이트의 메모리를 차지
  • 또한 ArrayList 자체도 내부 배열과 관련된 오버헤드를 가지며, 배열 크기 확장을 위한 여유 공간을 확보하기도 한다

 

 

 

아이템 52. mutable 컬렉션 사용을 고려하라

 

생략

반응형