What does MappedByteBuffer.force() do, and why is it important for durability of memory-mapped writes?
answer
- mapped writes -> page cache (dirty), written back lazily
- force() = synchronous flush to device, like fsync/msync
- PRIVATE mappings: force() is a no-op
- Java 13+ force(index, length) for partial flush
- batch + checkpoint; don't force per write
basics
~20 sWhen you write through a memory-mapped file, the changes sit in memory and the OS decides when to save them to disk. force() tells the OS to flush those changes to disk now, so they aren't lost if the program or machine crashes.
solid answer
~50 sWrites through a READ_WRITE MappedByteBuffer modify pages in the OS page cache and are marked dirty; the OS writes them back to the storage device asynchronously on its own schedule. That means after a put() returns, the data is in memory but not necessarily durable. force() requests the OS to flush the mapping's dirty pages to the underlying device synchronously, similar to fsync — so the change survives a crash or power loss. It's essential whenever you need durability guarantees, e.g. a write-ahead log or a database file. Caveats: force() is best-effort with respect to the device's own caches; it has no effect on PRIVATE mappings (their changes are never persisted); and it can be expensive, so high-throughput systems batch writes and call force() at controlled checkpoints rather than after every mutation. Java 13+ also adds region-scoped force(index, length) to flush only part of a large mapping.
go deeper
Knows force() saves memory-mapped changes to disk so they aren't lost on a crash.
Explains the dirty-page/page-cache model and that without force() write-back is deferred by the OS.
Distinguishes process crash vs. power loss, knows force() is fsync-like and best-effort wrt device caches, and that PRIVATE can't be forced; uses region-scoped force().
Designs durability protocols (WAL, group commit, checkpoints) balancing flush cost vs. throughput, accounts for device cache semantics and ordering, and reasons about end-to-end crash consistency.
## Why a write isn't durable immediately When you do `buf.put(0, (byte) 7)` on a READ_WRITE `MappedByteBuffer`, you modify a page that lives in the operating system's **page cache** — a region of RAM the OS uses to cache file data. The page is now **dirty** (changed but not yet on disk). For performance, the OS does **not** write dirty pages to the storage device right away; it batches and defers write-back. So the moment your `put` returns, the new value exists only in volatile memory. If the process crashes, the OS will still flush it eventually (the data is in the kernel, not your process). But if the **machine** loses power or kernel-panics before write-back, the change is **lost**. This is the gap **durability** addresses: a write is *durable* once it has reached non-volatile storage and would survive a crash. ## What force() does `MappedByteBuffer.force()` asks the OS to flush this mapping's dirty pages to the underlying device synchronously — conceptually the same as calling `fsync`/`msync` on the file. After `force()` returns successfully, the modified bytes have been handed to the storage device, so a subsequent power failure should not lose them. ```java MappedByteBuffer buf = ch.map(READ_WRITE, 0, size); buf.putLong(offset, value); buf.force(); // now value is (best-effort) durable ``` Java 13 added a **region-scoped** overload, `force(int index, int length)`, so you can flush just the part of a large mapping you changed instead of the whole thing — a meaningful optimization for big files. ## Important caveats 1. **Device caches.** `force()` flushes to the *device*, but many disks/SSDs have their own volatile write caches. Whether bytes are truly on the platters/NAND depends on the device honoring cache-flush commands and the storage stack's configuration. So `force()` is durability *as far as the OS can guarantee*, not an absolute physics promise. 2. **No effect on PRIVATE mappings.** A PRIVATE (copy-on-write) mapping's changes are never written back to the file, so `force()` cannot persist them — it is effectively a no-op for those pages. 3. **Cost.** A flush is a synchronous I/O barrier; doing it after every small write destroys throughput. Real systems (databases, message logs) batch many writes and `force()` at a **checkpoint** or **commit boundary** (e.g., group commit) to amortize the cost. 4. **Metadata vs. data.** Like fsync, whether file *metadata* (e.g., size changes) is also flushed can matter; for mappings you're flushing the mapped data pages. ## How this compares to channel.force() `FileChannel.force(boolean metaData)` flushes the whole file's pending writes (optionally including metadata). `MappedByteBuffer.force()` is the mapping-specific equivalent for pages you dirtied through the buffer. Both ultimately drive the OS to push data to the device. ## Summary Mapped writes land in the page cache and are written back lazily, so they aren't durable until flushed. `force()` synchronously pushes the mapping's dirty pages to the device for crash durability. Mind device caches, that PRIVATE mappings can't be forced, and the cost — batch and checkpoint instead of forcing per write.
- After put() returns without force(), can a process crash lose the data?A pure process crash usually won't lose it, because the dirty page lives in the OS page cache (kernel memory), and the OS will still flush it later. What loses it is a power failure or kernel panic before write-back. force() closes that window.
- Why do databases not call force() after every row write?force() is a synchronous I/O barrier and is expensive. Databases batch writes and flush at commit/checkpoint boundaries (e.g., group commit), trading a tiny durability window for much higher throughput while still meeting their durability contract.
Writing through the mapping is like jotting edits on a draft kept on your desk; force() is mailing the final copy to the archive. Until you mail it, a fire in your office (a crash) could destroy the edits.
saying these in an interview costs you the question
- Claiming a mapped put() is immediately durable on disk without force()
- Believing force() guarantees bytes are physically on the platter regardless of device write caches
- Expecting force() to persist a PRIVATE mapping's changes
- Calling force() after every tiny write in a hot path and wondering why throughput collapsed