From ead6a4d6af83b2eadac2552a49d9cad67afc5c50 Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Mon, 29 Jun 2026 20:14:07 -0700 Subject: [PATCH 1/6] Clarify Windows memory map file locking --- README.md | 34 ++++++++++++++---------- src/main/java/com/maxmind/db/Reader.java | 17 ++++++++---- 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 32a5461e..f760fd9d 100644 --- a/README.md +++ b/README.md @@ -41,10 +41,10 @@ this package directly. To use the API, you must first create a `Reader` object. The constructor for the reader object takes a `File` representing your MaxMind DB. Optionally you -may pass a second parameter with a `FileMode` with a value of `MEMORY_MAP` or -`MEMORY`. The default mode is `MEMORY_MAP`, which maps the file to virtual -memory. This often provides performance comparable to loading the file into -real memory with `MEMORY`. +may pass a second parameter with a `FileMode` with a value of `MEMORY_MAPPED` +or `MEMORY`. The default mode is `MEMORY_MAPPED`, which maps the file to +virtual memory. This often provides performance comparable to loading the file +into real memory with `MEMORY`. To look up an IP address, pass the address as an `InetAddress` to the `get` method on `Reader`, along with the class of the object you want to @@ -251,16 +251,22 @@ threads. ### File Lock on Windows ### -By default, this API uses the `MEMORY_MAP` mode, which memory maps the file. -On Windows, this may create an exclusive lock on the file that prevents it -from being renamed or deleted. Due to the implementation of memory mapping in -Java, this lock will not be released when the `DatabaseReader` is closed; it -will only be released when the object and the `MappedByteBuffer` it uses are -garbage collected. Older JVM versions may also not release the lock on exit. - -To work around this problem, use the `MEMORY` mode or try upgrading your JVM -version. You may also call `System.gc()` after dereferencing the -`DatabaseReader` object to encourage the JVM to garbage collect sooner. +By default, this API uses the `MEMORY_MAPPED` mode, which memory maps the file. +On Windows, a live memory mapping may prevent the file from being renamed, +replaced, or deleted. This is not a Java `FileLock`, but it can have similar +effects when updating a database file in place. + +Closing the `Reader` releases this library's reference to the mapped buffer, +but Java does not provide a supported way to unmap the underlying +`MappedByteBuffer` immediately. The mapping remains valid until the buffer +becomes unreachable and is garbage collected. Any outstanding lookup or +`Networks` iterator may also keep a duplicate buffer reachable. + +To avoid this behavior, use the `MEMORY` mode. If you must use +`MEMORY_MAPPED`, close and dereference the `Reader` and any iterators that were +created from it before replacing the file. You may call `System.gc()` to +encourage earlier cleanup, but garbage collection is not guaranteed to run +immediately. ### Packaging Database in a JAR ### diff --git a/src/main/java/com/maxmind/db/Reader.java b/src/main/java/com/maxmind/db/Reader.java index c1d16fef..29a83a3e 100644 --- a/src/main/java/com/maxmind/db/Reader.java +++ b/src/main/java/com/maxmind/db/Reader.java @@ -38,10 +38,15 @@ public enum FileMode { * The default file mode. This maps the database to virtual memory. This * often provides similar performance to loading the database into real * memory without the overhead. + * + *

On Windows, a live memory mapping may prevent the database file + * from being renamed, replaced, or deleted until the mapped buffer is + * garbage collected. */ MEMORY_MAPPED, /** - * Loads the database into memory when the reader is constructed. + * Loads the database into memory when the reader is constructed. This + * avoids keeping a live memory mapping of the database file. */ MEMORY } @@ -497,10 +502,12 @@ public Metadata getMetadata() { *

*

* If you are using FileMode.MEMORY_MAPPED, this will - * not unmap the underlying file due to a limitation in Java's - * MappedByteBuffer. It will however set the reference to - * the buffer to null, allowing the garbage collector to - * collect it. + * release this reader's reference to the mapped buffer, allowing the + * garbage collector to collect it when no other references remain. Java + * does not provide a supported way to unmap a + * MappedByteBuffer immediately. On Windows, this means the + * database file may remain unavailable for rename, replacement, or + * deletion until the mapped buffer is garbage collected. *

* * @throws IOException if an I/O error occurs. From 3b9ddb18b0bf27b397ce01263ec22e8b68c81307 Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Tue, 30 Jun 2026 07:12:21 -0700 Subject: [PATCH 2/6] Update mise lockfile --- mise.lock | 90 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 50 insertions(+), 40 deletions(-) diff --git a/mise.lock b/mise.lock index fa8fae3c..0bdccfe6 100644 --- a/mise.lock +++ b/mise.lock @@ -1,94 +1,104 @@ -# @generated - this file is auto-generated by `mise lock` https://mise.jdx.dev/dev-tools/mise-lock.html +# @generated - this file is auto-generated by `mise lock` https://mise.en.dev/dev-tools/mise-lock.html [[tools.hugo]] -version = "0.161.1" +version = "0.163.3" backend = "aqua:gohugoio/hugo" [tools.hugo."platforms.linux-arm64"] -checksum = "sha256:382371ec3208236fb854ced51781f859b6c27a7d066b8fe90594eba14ba76d00" -url = "https://github.com/gohugoio/hugo/releases/download/v0.161.1/hugo_0.161.1_linux-arm64.tar.gz" +checksum = "sha256:a4185cf0308ff3a61a2828563f70f476fcef30d02e9b00fb562eb1bd085195a5" +url = "https://github.com/gohugoio/hugo/releases/download/v0.163.3/hugo_0.163.3_linux-arm64.tar.gz" [tools.hugo."platforms.linux-arm64-musl"] -checksum = "sha256:382371ec3208236fb854ced51781f859b6c27a7d066b8fe90594eba14ba76d00" -url = "https://github.com/gohugoio/hugo/releases/download/v0.161.1/hugo_0.161.1_linux-arm64.tar.gz" +checksum = "sha256:a4185cf0308ff3a61a2828563f70f476fcef30d02e9b00fb562eb1bd085195a5" +url = "https://github.com/gohugoio/hugo/releases/download/v0.163.3/hugo_0.163.3_linux-arm64.tar.gz" [tools.hugo."platforms.linux-x64"] -checksum = "sha256:fae28bf7909c1a42d1365b89d2e9e3d4194fbe5968ae0dd5504f562381018a1d" -url = "https://github.com/gohugoio/hugo/releases/download/v0.161.1/hugo_0.161.1_linux-amd64.tar.gz" +checksum = "sha256:ec422258f9a4ffc241de8707297e32311cd86fcc9b2813632617ff4d44935d91" +url = "https://github.com/gohugoio/hugo/releases/download/v0.163.3/hugo_0.163.3_linux-amd64.tar.gz" [tools.hugo."platforms.linux-x64-musl"] -checksum = "sha256:fae28bf7909c1a42d1365b89d2e9e3d4194fbe5968ae0dd5504f562381018a1d" -url = "https://github.com/gohugoio/hugo/releases/download/v0.161.1/hugo_0.161.1_linux-amd64.tar.gz" +checksum = "sha256:ec422258f9a4ffc241de8707297e32311cd86fcc9b2813632617ff4d44935d91" +url = "https://github.com/gohugoio/hugo/releases/download/v0.163.3/hugo_0.163.3_linux-amd64.tar.gz" [tools.hugo."platforms.macos-arm64"] -checksum = "sha256:b12e1cbebacf61f9cf67e0046c835142e70c829da7c16b05c1ec64a68885ee80" -url = "https://github.com/gohugoio/hugo/releases/download/v0.161.1/hugo_0.161.1_darwin-universal.pkg" +checksum = "sha256:a59f749a6dbf613da9ec9c51ab670add0ca72b7eed6590bbff779a6fd6b70f0c" +url = "https://github.com/gohugoio/hugo/releases/download/v0.163.3/hugo_0.163.3_darwin-universal.pkg" [tools.hugo."platforms.macos-x64"] -checksum = "sha256:b12e1cbebacf61f9cf67e0046c835142e70c829da7c16b05c1ec64a68885ee80" -url = "https://github.com/gohugoio/hugo/releases/download/v0.161.1/hugo_0.161.1_darwin-universal.pkg" +checksum = "sha256:a59f749a6dbf613da9ec9c51ab670add0ca72b7eed6590bbff779a6fd6b70f0c" +url = "https://github.com/gohugoio/hugo/releases/download/v0.163.3/hugo_0.163.3_darwin-universal.pkg" [tools.hugo."platforms.windows-x64"] -checksum = "sha256:7f8d030b37600c60bf2a782611257e6a768934fbe7724c1f3a1a501e6724cf0d" -url = "https://github.com/gohugoio/hugo/releases/download/v0.161.1/hugo_0.161.1_windows-amd64.zip" +checksum = "sha256:f330476374b604d3bc0e33a929ff476e9623bc62938f16846e832620545cd705" +url = "https://github.com/gohugoio/hugo/releases/download/v0.163.3/hugo_0.163.3_windows-amd64.zip" [[tools.java]] version = "26.0.1" backend = "core:java" -[tools.java."platforms.linux-x64"] -checksum = "sha256:2f2802d57b5fc414f1ddf6648ba12cc9a6454cf67b32ac95407c018f2e6ab0b0" -url = "https://download.java.net/java/GA/jdk26.0.1/458fda22e4c54d5ba572ab8d2b22eb83/8/GPL/openjdk-26.0.1_linux-x64_bin.tar.gz" +[tools.java.options] +shorthand_vendor = "openjdk" [[tools.lychee]] -version = "0.23.0" +version = "0.24.2" backend = "aqua:lycheeverse/lychee" [tools.lychee."platforms.linux-arm64"] -checksum = "sha256:97eb93b02a7d78a752fc33e5b0983439ccaadbf3db952b68a0a4401acd92e6e0" -url = "https://github.com/lycheeverse/lychee/releases/download/lychee-v0.23.0/lychee-aarch64-unknown-linux-gnu.tar.gz" +checksum = "sha256:5d0b0e3aeab240f41920c633a6eaf97599be6eedda034b36e858ede7dba5e535" +url = "https://github.com/lycheeverse/lychee/releases/download/lychee-v0.24.2/lychee-aarch64-unknown-linux-musl.tar.gz" [tools.lychee."platforms.linux-arm64-musl"] -checksum = "sha256:97eb93b02a7d78a752fc33e5b0983439ccaadbf3db952b68a0a4401acd92e6e0" -url = "https://github.com/lycheeverse/lychee/releases/download/lychee-v0.23.0/lychee-aarch64-unknown-linux-gnu.tar.gz" +checksum = "sha256:5d0b0e3aeab240f41920c633a6eaf97599be6eedda034b36e858ede7dba5e535" +url = "https://github.com/lycheeverse/lychee/releases/download/lychee-v0.24.2/lychee-aarch64-unknown-linux-musl.tar.gz" [tools.lychee."platforms.linux-x64"] -checksum = "sha256:5538440d2c69a45a0a09983271e5dee0c2fe7137d8035d25b2632e10a66a090a" -url = "https://github.com/lycheeverse/lychee/releases/download/lychee-v0.23.0/lychee-x86_64-unknown-linux-musl.tar.gz" +checksum = "sha256:73657a111819a30c47c08352896796f23d64e4eb2b3ed39b6d32149241566fc5" +url = "https://github.com/lycheeverse/lychee/releases/download/lychee-v0.24.2/lychee-x86_64-unknown-linux-musl.tar.gz" [tools.lychee."platforms.linux-x64-musl"] -checksum = "sha256:5538440d2c69a45a0a09983271e5dee0c2fe7137d8035d25b2632e10a66a090a" -url = "https://github.com/lycheeverse/lychee/releases/download/lychee-v0.23.0/lychee-x86_64-unknown-linux-musl.tar.gz" +checksum = "sha256:73657a111819a30c47c08352896796f23d64e4eb2b3ed39b6d32149241566fc5" +url = "https://github.com/lycheeverse/lychee/releases/download/lychee-v0.24.2/lychee-x86_64-unknown-linux-musl.tar.gz" [tools.lychee."platforms.macos-arm64"] -checksum = "sha256:4c8034900e11083b68ac6f6582c377ff1f704e268991999e09d717973e493e7f" -url = "https://github.com/lycheeverse/lychee/releases/download/lychee-v0.23.0/lychee-arm64-macos.dmg" +checksum = "sha256:c9d3740ea2d891854d37116c9fba840f37b6e7c89d330e7db84ac333631c4977" +url = "https://github.com/lycheeverse/lychee/releases/download/lychee-v0.24.2/lychee-aarch64-apple-darwin.tar.gz" + +[tools.lychee."platforms.macos-x64"] +checksum = "sha256:887503a9cff667d322b8d0892b40bf49976eb9507af8483220a3706cdad55978" +url = "https://github.com/lycheeverse/lychee/releases/download/lychee-v0.24.2/lychee-x86_64-apple-darwin.tar.gz" [tools.lychee."platforms.windows-x64"] -checksum = "sha256:0fda7ff0a60c0250939fc25361c2d4e6e7853c31c996733fdd5a1dd760bcb824" -url = "https://github.com/lycheeverse/lychee/releases/download/lychee-v0.23.0/lychee-x86_64-windows.exe" +checksum = "sha256:32975d1493ee1a975d6bb41e4fb56fe419cb442ded628bb772ba2e614acfacad" +url = "https://github.com/lycheeverse/lychee/releases/download/lychee-v0.24.2/lychee-x86_64-pc-windows-msvc.zip" [[tools.maven]] -version = "3.9.15" +version = "3.9.16" backend = "aqua:apache/maven" [tools.maven."platforms.linux-arm64"] -url = "https://archive.apache.org/dist/maven/maven-3/3.9.15/binaries/apache-maven-3.9.15-bin.tar.gz" +checksum = "sha512:831a8591fe20c8243b1dbe7d71e3244f31d1665b0804b2e825e38cbbe5ce0cafb8338851f90780735568773e0a6cd07bbec107cda0b896b008b861075358b6f6" +url = "https://archive.apache.org/dist/maven/maven-3/3.9.16/binaries/apache-maven-3.9.16-bin.tar.gz" [tools.maven."platforms.linux-arm64-musl"] -url = "https://archive.apache.org/dist/maven/maven-3/3.9.15/binaries/apache-maven-3.9.15-bin.tar.gz" +checksum = "sha512:831a8591fe20c8243b1dbe7d71e3244f31d1665b0804b2e825e38cbbe5ce0cafb8338851f90780735568773e0a6cd07bbec107cda0b896b008b861075358b6f6" +url = "https://archive.apache.org/dist/maven/maven-3/3.9.16/binaries/apache-maven-3.9.16-bin.tar.gz" [tools.maven."platforms.linux-x64"] -url = "https://archive.apache.org/dist/maven/maven-3/3.9.15/binaries/apache-maven-3.9.15-bin.tar.gz" +checksum = "sha512:831a8591fe20c8243b1dbe7d71e3244f31d1665b0804b2e825e38cbbe5ce0cafb8338851f90780735568773e0a6cd07bbec107cda0b896b008b861075358b6f6" +url = "https://archive.apache.org/dist/maven/maven-3/3.9.16/binaries/apache-maven-3.9.16-bin.tar.gz" [tools.maven."platforms.linux-x64-musl"] -url = "https://archive.apache.org/dist/maven/maven-3/3.9.15/binaries/apache-maven-3.9.15-bin.tar.gz" +checksum = "sha512:831a8591fe20c8243b1dbe7d71e3244f31d1665b0804b2e825e38cbbe5ce0cafb8338851f90780735568773e0a6cd07bbec107cda0b896b008b861075358b6f6" +url = "https://archive.apache.org/dist/maven/maven-3/3.9.16/binaries/apache-maven-3.9.16-bin.tar.gz" [tools.maven."platforms.macos-arm64"] -url = "https://archive.apache.org/dist/maven/maven-3/3.9.15/binaries/apache-maven-3.9.15-bin.tar.gz" +checksum = "sha512:831a8591fe20c8243b1dbe7d71e3244f31d1665b0804b2e825e38cbbe5ce0cafb8338851f90780735568773e0a6cd07bbec107cda0b896b008b861075358b6f6" +url = "https://archive.apache.org/dist/maven/maven-3/3.9.16/binaries/apache-maven-3.9.16-bin.tar.gz" [tools.maven."platforms.macos-x64"] -url = "https://archive.apache.org/dist/maven/maven-3/3.9.15/binaries/apache-maven-3.9.15-bin.tar.gz" +checksum = "sha512:831a8591fe20c8243b1dbe7d71e3244f31d1665b0804b2e825e38cbbe5ce0cafb8338851f90780735568773e0a6cd07bbec107cda0b896b008b861075358b6f6" +url = "https://archive.apache.org/dist/maven/maven-3/3.9.16/binaries/apache-maven-3.9.16-bin.tar.gz" [tools.maven."platforms.windows-x64"] -url = "https://archive.apache.org/dist/maven/maven-3/3.9.15/binaries/apache-maven-3.9.15-bin.tar.gz" +checksum = "sha512:831a8591fe20c8243b1dbe7d71e3244f31d1665b0804b2e825e38cbbe5ce0cafb8338851f90780735568773e0a6cd07bbec107cda0b896b008b861075358b6f6" +url = "https://archive.apache.org/dist/maven/maven-3/3.9.16/binaries/apache-maven-3.9.16-bin.tar.gz" From cc9622d94e078bee4abe6982a9572d253eee1d77 Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Tue, 30 Jun 2026 14:26:38 +0000 Subject: [PATCH 3/6] Fix typos in Reader Javadoc Correct "HastMap" -> "HashMap" and "databased" -> "database" in the get(), getRecord(), and networks() Javadoc. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/main/java/com/maxmind/db/Reader.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/maxmind/db/Reader.java b/src/main/java/com/maxmind/db/Reader.java index 29a83a3e..a572bfb2 100644 --- a/src/main/java/com/maxmind/db/Reader.java +++ b/src/main/java/com/maxmind/db/Reader.java @@ -245,11 +245,11 @@ long record = traverseResult[0]; * in an IPv6 database. networks() iterates over the canonical locations and * not the aliases. To include the aliases, you can set includeAliasedNetworks to true. * - * @param Represents the data type(e.g., Map, HastMap, etc.). + * @param Represents the data type(e.g., Map, HashMap, etc.). * @param typeParameterClass The type of data returned by the iterator. * @return Networks The Networks iterator. * @throws InvalidNetworkException Exception for using an IPv6 network in ipv4-only database. - * @throws ClosedDatabaseException Exception for a closed databased. + * @throws ClosedDatabaseException Exception for a closed database. * @throws InvalidDatabaseException Exception for an invalid database. */ public Networks networks(Class typeParameterClass) throws @@ -264,11 +264,11 @@ public Networks networks(Class typeParameterClass) throws * separately. To set the iteration over the IPv4 networks once, use the * includeAliasedNetworks option. * - * @param Represents the data type(e.g., Map, HastMap, etc.). + * @param Represents the data type(e.g., Map, HashMap, etc.). * @param includeAliasedNetworks Enable including aliased networks. * @return Networks The Networks iterator. * @throws InvalidNetworkException Exception for using an IPv6 network in ipv4-only database. - * @throws ClosedDatabaseException Exception for a closed databased. + * @throws ClosedDatabaseException Exception for a closed database. * @throws InvalidDatabaseException Exception for an invalid database. */ public Networks networks( @@ -331,13 +331,13 @@ private long findIpV4StartNode(Buffer buffer) * separately. To only iterate over the IPv4 networks once, use the * includeAliasedNetworks option. * - * @param Represents the data type(e.g., Map, HastMap, etc.). + * @param Represents the data type(e.g., Map, HashMap, etc.). * @param network Specifies the network to be iterated. * @param includeAliasedNetworks Boolean for including aliased networks. * @param typeParameterClass The type of data returned by the iterator. * @return Networks * @throws InvalidNetworkException Exception for using an IPv6 network in ipv4-only database. - * @throws ClosedDatabaseException Exception for a closed databased. + * @throws ClosedDatabaseException Exception for a closed database. * @throws InvalidDatabaseException Exception for an invalid database. */ public Networks networksWithin( From 52449bd1b4ff005b3804e030e19a190917245c8f Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Tue, 30 Jun 2026 14:26:38 +0000 Subject: [PATCH 4/6] Correct misleading capacity Javadoc in CHMCache The CHMCache(int) parameter doc claimed the cache evicts entries once capacity is reached. There is no eviction policy: as the class-level Javadoc and the implementation show, the cache simply stops accepting new entries once it is full. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/main/java/com/maxmind/db/CHMCache.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/maxmind/db/CHMCache.java b/src/main/java/com/maxmind/db/CHMCache.java index 250b45b1..97ff6776 100644 --- a/src/main/java/com/maxmind/db/CHMCache.java +++ b/src/main/java/com/maxmind/db/CHMCache.java @@ -27,8 +27,8 @@ public CHMCache() { * Creates a new cache with the specified capacity. * * @param capacity - * the maximum number of elements the cache can hold before - * starting to evict them + * the maximum number of elements the cache can hold before it + * stops accepting new entries */ public CHMCache(int capacity) { this.capacity = capacity; From 049e3a16618acf114705fe6dd280ffc7a46b9a97 Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Tue, 30 Jun 2026 14:41:57 +0000 Subject: [PATCH 5/6] Fix incorrect type name in Networks Javadoc The next() Javadoc referred to "DataRecord"; the method returns DatabaseRecord. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/main/java/com/maxmind/db/Networks.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/maxmind/db/Networks.java b/src/main/java/com/maxmind/db/Networks.java index af61da65..0345e95e 100644 --- a/src/main/java/com/maxmind/db/Networks.java +++ b/src/main/java/com/maxmind/db/Networks.java @@ -48,9 +48,9 @@ public final class Networks implements Iterator> { } /** - * Returns the next DataRecord. + * Returns the next DatabaseRecord. * - * @return The next DataRecord. + * @return The next DatabaseRecord. * @throws NetworksIterationException An exception when iterating over the networks. */ @Override From bcd7d74e8230089f2bc746586c50907b894511a7 Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Tue, 30 Jun 2026 14:55:41 +0000 Subject: [PATCH 6/6] fixup! Update mise lockfile --- lychee.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lychee.toml b/lychee.toml index cdd42143..5f585005 100644 --- a/lychee.toml +++ b/lychee.toml @@ -5,7 +5,7 @@ # lychee './**/*.md' './src/**/*.java' './pom.xml' # Include URL fragments in checks -include_fragments = true +include_fragments = "full" # Don't allow any redirects, so links that have moved are surfaced and updated # to their canonical destination.