Skip to content

Commit 33979eb

Browse files
committed
Avoid off-heap memory growth from JDK direct buffer caching
Read database files via Files.readAllBytes and pass as an InputStream to DatabaseReader.Builder, instead of passing a File which internally uses FileChannel.read into a heap ByteBuffer. The FileChannel path causes the JDK to allocate and cache temporary direct ByteBuffers in thread-local storage (sun.nio.ch.Util.BufferCache), leading to off-heap memory growth in scenarios where the database is initialized repeatedly.
1 parent 94d090c commit 33979eb

File tree

1 file changed

+9
-4
lines changed

1 file changed

+9
-4
lines changed

src/main/scala/com.snowplowanalytics.maxmind.iplookups/IpLookups.scala

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,16 @@
1212
*/
1313
package com.snowplowanalytics.maxmind.iplookups
1414

15-
import java.io.File
15+
import java.io.{ByteArrayInputStream, File}
1616
import java.net.InetAddress
17+
import java.nio.file.Files
1718

1819
import cats.{Eval, Id, Monad}
1920
import cats.effect.Sync
2021
import cats.syntax.flatMap._
2122
import cats.syntax.functor._
2223
import cats.syntax.option._
2324
import com.maxmind.db.CHMCache
24-
import com.maxmind.db.Reader.FileMode
2525
import com.maxmind.geoip2.DatabaseReader
2626
import com.snowplowanalytics.lrumap.{CreateLruMap, LruMap}
2727

@@ -226,14 +226,19 @@ class IpLookups[F[_]: Monad] private[iplookups] (
226226
/**
227227
* Get a LookupService from a database file
228228
*
229-
* Note: In MEMORY mode, .build() performs blocking I/O by reading the entire database from disk into memory.
229+
* Reads the file into a byte array using java.nio.file.Files.readAllBytes, then passes it
230+
* to DatabaseReader as an InputStream. This avoids FileChannel.read into a heap ByteBuffer,
231+
* which causes the JDK to allocate and cache temporary direct ByteBuffers in thread-local
232+
* storage (sun.nio.ch.Util.BufferCache), leading to off-heap memory growth.
230233
*
231234
* @param serviceFile The database file
232235
* @return LookupService
233236
*/
234237
private def getService(serviceFile: Option[File]): Option[DatabaseReader] =
235238
serviceFile.map { f =>
236-
val builder = new DatabaseReader.Builder(f).fileMode(FileMode.MEMORY)
239+
val bytes = Files.readAllBytes(f.toPath)
240+
val stream = new ByteArrayInputStream(bytes)
241+
val builder = new DatabaseReader.Builder(stream)
237242
(
238243
if (memCache) builder.withCache(new CHMCache())
239244
else builder

0 commit comments

Comments
 (0)