Python mmap: Memory-Mapped Files

Published on: August 9, 2026
Reading time: 6 minutes
Hard drive representing memory-mapped files with Python mmap

Large files can be accessed without manually reading the entire contents into one Python bytes object. Python mmap creates a mapping between a file region and the process virtual address space. The resulting object behaves both like a file and like a mutable byte array.

This technique is useful for pattern search, binary indexes, embedded storage, interprocess communication, and fixed-region updates. It does not make every workload automatically faster, and it requires careful handling of access rights, page alignment, concurrent writers, file size, and persistence. This guide covers read, write, copy-on-write, anonymous mappings, platform differences, flushing, and resource management.

It complements our guides to fileinput, linecache, tempfile, filecmp, and weakref.

How mapping works

The operating system associates file pages with virtual-memory addresses. Pages are loaded as needed, and the kernel manages caching and eviction. Python code can then use indexes, slices, searches, or file-like methods.

The disk cost still exists. Page faults, random access, and memory pressure can dominate performance, especially when the file is much larger than available RAM.

Read-only whole-file mapping

import mmap

with open("data.bin", "rb") as file:
    with mmap.mmap(
        file.fileno(),
        length=0,
        access=mmap.ACCESS_READ,
    ) as memory:
        print(memory[:16])

length=0 requests the current file size on supported platforms. Windows cannot create this mapping for an empty file.

Read-only access

ACCESS_READ prevents mutation. Assignment raises TypeError.

memory[0] = 65  # TypeError

Prefer read-only access for search and analysis. It reduces the chance of accidental file corruption.

Persistent write access

with open("data.bin", "r+b") as file:
    with mmap.mmap(
        file.fileno(),
        0,
        access=mmap.ACCESS_WRITE,
    ) as memory:
        memory[0:4] = b"HEAD"
        memory.flush()

With ACCESS_WRITE, changes affect both the mapping and the underlying file. Slice replacement must use the same length; mmap is not a dynamically growing list.

Copy-on-write

with open("data.bin", "rb") as file:
    with mmap.mmap(
        file.fileno(),
        0,
        access=mmap.ACCESS_COPY,
    ) as memory:
        memory[0:4] = b"TEST"

Changes remain private and do not update the file. This is useful for experiments and temporary transformations, but it is not the same as making a full independent file copy in advance.

Flush buffered file writes first

If a writable Python file object contains buffered modifications, call flush() before creating the map.

file.write(b"content")
file.flush()
memory = mmap.mmap(file.fileno(), 0)

This ensures that local buffered data is visible to the mapping mechanism.

Searching bytes

position = memory.find(b"ERROR")
if position != -1:
    print("found at", position)

find() and rfind() accept start and end ranges. The re module can also search directly through a mapped object.

Regular expressions

import re

for match in re.finditer(rb"ID:\d+", memory):
    print(match.start(), match.group())

Use byte patterns rather than Unicode strings. Text interpretation requires a known encoding and care around multibyte character boundaries.

File-like methods

Mmap objects maintain a current position and provide read(), readline(), seek(), tell(), and write().

memory.seek(0)
first_line = memory.readline()
position = memory.tell()

Since Python 3.13, seek() returns the new absolute position.

Indexing and slicing

first = memory[0]       # integer
header = memory[0:16]  # bytes

Assigning an index requires an integer byte value. Assigning a slice requires a bytes-like object with compatible length.

Flushing changes

flush() asks the system to write modified pages back.

memory.flush()

An offset and size can be supplied, but the offset must be aligned to PAGESIZE or ALLOCATIONGRANULARITY. Handle exceptions rather than assuming persistence succeeded.

Durability is not a transaction

Flushing the mapping, syncing the file descriptor, and guaranteeing crash consistency are related but distinct topics. Transactional applications must understand fsync, write ordering, metadata updates, and atomic replacement.

Mmap does not automatically turn a file into a reliable database.

Aligned offsets

The constructor offset must be a multiple of ALLOCATIONGRANULARITY. To map an arbitrary region, align the starting offset downward and maintain an internal delta.

gran = mmap.ALLOCATIONGRANULARITY
base = start // gran * gran
delta = start - base
length = delta + requested_size

Access the desired bytes through memory[delta:delta+requested_size].

File size and mapping size

The mapping length defines the visible region. size() can return the underlying file size, which may exceed the mapped area. len(memory) reports the mapped length.

Do not access beyond the mapping. Concurrent file-size changes can have platform-specific and dangerous consequences.

Resizing

resize() changes the map and, where applicable, the file. Read-only, copy-on-write, or trackfd=False maps cannot be resized.

On Windows, other maps for the same file can block resizing. Coordinate the operation exclusively.

trackfd on Unix

Since Python 3.13, the Unix constructor accepts trackfd=False. The descriptor is not duplicated, reducing descriptor usage, but size() and resize() stop working.

Use it only when descriptor pressure is real and the application owns the lifecycle carefully.

Anonymous memory

with mmap.mmap(-1, 4096) as memory:
    memory.write(b"temporary data")

Passing -1 creates a region not backed by a regular file. It can act as a buffer and, under some process models, as shared memory.

Interprocess sharing

On Unix, an anonymous map created before fork() can be shared between parent and child. For portable application code, multiprocessing.shared_memory may expose intent more clearly.

Shared memory provides no automatic synchronization. Use locks, semaphores, or a carefully designed atomic protocol.

MAP_SHARED and MAP_PRIVATE

The low-level Unix constructor supports MAP_SHARED for mappings visible to other processes and MAP_PRIVATE for copy-on-write behavior.

Do not specify access together with explicit flags and prot; that combination is invalid.

Windows differences

The Windows constructor accepts an optional tagname for named mappings, but avoiding it improves portability. Offsets still need allocation-granularity alignment.

Mapping a length larger than the file can extend the file on Windows. Do not rely on this behavior in cross-platform code.

Kernel advice with madvise

On supported systems, madvise() can describe an expected access pattern.

if hasattr(memory, "madvise"):
    memory.madvise(mmap.MADV_SEQUENTIAL)

Available constants differ by operating system. Treat this as an optional optimization and benchmark it.

Sequential and random access

Mapping does not eliminate storage latency. Sequential scans benefit from prefetching, while random access to data much larger than RAM can cause many page faults.

Benchmark representative data and observe resident memory, page faults, and tail latency.

Concurrent writers

Two processes can modify the same region and violate application invariants. Memory coherence does not protect multi-step logical updates.

Define headers, versions, locks, checksums, and a commit protocol. Never assume a sequence of byte writes is atomic.

External truncation

If another process truncates a mapped file, later access can fail severely or behave differently across systems. Control ownership and the file lifecycle while mappings exist.

Closing resources

Use context managers. Closing an mmap does not close the original Python file object.

with open("data.bin", "rb") as file:
    with mmap.mmap(file.fileno(), 0, access=mmap.ACCESS_READ) as memory:
        process(memory)

Outstanding memory views can prevent closing. Release them before leaving the context.

Security

Mapping a file does not validate its contents. Lengths, offsets, and internal records can be malicious. Verify all boundaries before using values from the file to calculate slices.

Do not map devices or special files without understanding their behavior. Creating a map emits a Python auditing event.

Testing

Test empty files, small and large files, read-only maps, copy-on-write behavior, aligned regions, unsuccessful searches, flushing, controlled concurrency, and both Windows and Unix.

Common mistakes

  • Mapping an empty file on Windows.
  • Opening with an incompatible file mode.
  • Forgetting to flush buffered writes before mapping.
  • Using an unaligned offset.
  • Assuming flush() provides a transaction.
  • Assigning a slice of different length.
  • Resizing while other maps exist.
  • Sharing memory without synchronization.

Best practices

  • Default to ACCESS_READ.
  • Use copy-on-write for temporary edits.
  • Validate sizes and offsets.
  • Close maps with with.
  • Benchmark real access patterns.
  • Synchronize concurrent writers.
  • Test platform differences.
  • Do not treat mmap as a transactional database.

Conclusion

Python mmap provides flexible, efficient access to files and memory regions. It combines byte slicing, file methods, searching, and controlled sharing.

Performance depends on the access pattern and operating system. Use minimal permissions, align offsets, validate structures, and coordinate concurrency. Consult the official mmap documentation and the Unix mmap manual for lower-level details.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Source code representing parser tokens and constants with the Python token module
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python token: Parser Constants

    Learn Python token constants for lexical types, exact operators, indentation, f-strings, t-strings, and version-aware parsers.

    Ler mais

    Tempo de leitura: 7 minutos
    07/08/2026
    Source code representing reserved words and soft keywords in Python
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python keyword: Reserved Words

    Learn Python keyword to validate identifiers, reserved words, and soft keywords for the target interpreter version.

    Ler mais

    Tempo de leitura: 5 minutos
    07/08/2026
    Software architecture representing abstract base classes with Python abc
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python abc: Abstract Base Classes

    Learn Python abc to create abstract classes, required methods, virtual subclasses, and stable runtime contracts.

    Ler mais

    Tempo de leitura: 5 minutos
    06/08/2026
    Code and structures representing runtime types with the Python types module
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python types: Runtime Type Utilities

    Learn Python types for SimpleNamespace, MappingProxyType, runtime type names, and safe dynamic class creation.

    Ler mais

    Tempo de leitura: 5 minutos
    06/08/2026
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python sched: Schedule Events

    Learn Python sched to schedule events, control priorities, cancel tasks, and build recurring work with a monotonic clock.

    Ler mais

    Tempo de leitura: 7 minutos
    05/08/2026
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python atexit: Run Cleanup on Exit

    Learn Python atexit to run cleanup at shutdown, control LIFO order, and avoid problems with threads, signals, and handler exceptions.

    Ler mais

    Tempo de leitura: 6 minutos
    05/08/2026