대용량 데이터 처리
JVM에서 데이터를 처리하는 방법은 여러가지가 있다. 이번 포스팅에서는 InputStream에 대해 다뤄볼것이다.
첫 번째 방법은 ByteArrayInputStream을 사용해서 데이터를 읽는것이다. 이 방법은 간단하게 읽을 수 있지만 데이터를 모두 메모리에 올려 놓고 사용하기 때문에 데이터의 용량이 크지 않을때 사용하기 좋다. 대용량 데이터가 아니라면 편하게 사용할 수 있다.
fun readByByteArray(): InputStream {
val size = 512 * 1024 * 1024
val baos = ByteArrayOutputStream(size)
val random = Random(System.currentTimeMillis())
val buffer = ByteArray(1024 * 1024)
repeat(512) {
random.nextBytes(buffer)
baos.write(buffer)
}
return ByteArrayInputStream(baos.toByteArray())
}
위의 코드는 512MB 사이즈의 랜덤 바이트를 넣고 toByteArray로 그걸 복사해서 InputStream에 넣는다.
toByteArray는 새로운 byte배열을 복사해서 반환하므로 결과적으로 512MB + 512MB = 1024MB 의 힙 메모리를 쓰게 될 것이다.
두번째로는 파이프 기반 스트림 방식인 PipedInputStream 을 사용하는것이다.
첫 번째 방식이 한 번에 물을 붓는것이라면, 두 번째 방식은 호스로 연결해놓고 물을 계속 받는 방식과 같다.
이렇게 하면 메모리에 모든 데이터를 올려놓을 필요없이, 호스 내부에서 흐르고 있는 물, 버퍼의 크기 만큼만 메모리에 들고 있으면 된다.
fun readByStream(): InputStream {
val size = 512 * 1024 * 1024
val pis = PipedInputStream()
val pos = PipedOutputStream(pis)
val random = Random(System.currentTimeMillis())
Thread {
pos.use { out ->
val buffer = ByteArray(1024 * 1024)
repeat(512) {
random.nextBytes(buffer)
out.write(buffer)
}
}
}.start()
return pis
}
새 스레드가 out으로 데이터를 밀어넣고, 그것을 소비하는 스레드(보통 메인 스레드)에서 데이터를 받아간다.
실제로 두 방식의 차이가 성능에 무슨 차이가 있을까? Intellij Profiler로 메모리 사용량을 모니터링 해보았다.
한 번에 읽는 방식

예상했듯이, ByteArrayOutputStream을 만드는데만 556.52MB가 할당되었고, 마지막 baos에 쓰인 데이터를 toByteArray하는데 537.08MB가 들어 총 556.52 + 537.08 = 1093.6MB 가 필요했다.
지금은 파일을 읽는다고 가정하지만, DB에서 데이터를 가져오는 경우도 마찬가지로 ByteArrayInputStream을 사용하려면 모든 데이터를 메모리에 올려서 사용해야할 것이다.
실제로 메모리를 줄여서 실행해보면 OOM에러가 뜬다. (-Xms256m -Xmx256m 옵션으로 실행)

파이프 기반 스트림을 사용한다면

1.05MB 이게 끝이다. 파이프 내부 버퍼 사이즈 정도의 메모리만 사용한다.
결론
대용량 데이터를 처리할 땐 파이프 기반 스트림 처리 방식을 사용하자
'Java,Kotlin,SpringBoot' 카테고리의 다른 글
| [SpringBoot] Event 사용하기 (0) | 2024.12.08 |
|---|---|
| [SpringBoot] AbstractAggregateRoot로 DomainEvent 발행하기 (3) | 2024.11.27 |
| [SpringBoot] @Async로 비동기 작업 처리하기 (1) | 2024.09.19 |
| [SpringBoot] JPA에서 Enum 값 다루기(AttributeConverter) (3) | 2024.09.13 |
| [Java] ExecutorService와 Future (2) | 2024.08.20 |