Menu
DZone Microservices·May 12, 2026

Understanding Java Memory Growth in Docker on M1 Macs Due to Rosetta 2

This article investigates mysterious RSS memory growth in Java applications running in x86-64 Docker containers on ARM64 M1 Macs. It identifies Rosetta 2's translation cache for JIT-compiled code as the root cause, leading to the allocation of 128 MB executable memory regions. The key takeaway for system architects is to prioritize native ARM64 Docker images to avoid performance and memory overheads associated with emulation.

Read original on DZone Microservices

When deploying Java applications in Docker containers on M1 Macs, developers might encounter unexpected growth in Resident Set Size (RSS), even with stable heap usage. This phenomenon, initially perplexing, is attributed to the interaction between Java's Just-In-Time (JIT) compilation, Docker's emulation capabilities, and Apple's Rosetta 2 translation layer.

The Emulation Overhead: Rosetta 2's Role

The core of the problem lies in running x86-64 Docker images on ARM64 M1 Macs. Docker Desktop utilizes Rosetta 2 to translate x86-64 instructions to ARM64 at runtime. Java's JIT compiler dynamically generates x86-64 native code for frequently executed methods. Each time new x86-64 JIT-compiled code is executed, Rosetta 2 translates it to ARM64, caching these translations in executable memory regions (RWXP regions).

ℹ️

The "Perfect Storm" for RSS Growth

This specific memory growth occurs when all four conditions are met: an ARM64 host (M1 Mac), an x86-64 container, Rosetta 2 enabled (via Docker Desktop), and dynamic code generation (Java JIT compiler).

Understanding RWXP Memory Regions

Investigation using tools like `/proc/PID/maps` reveals anonymous, read-write-execute (RWXP) memory regions, each exactly 128 MB, containing ARM64 machine code. These regions are the Rosetta 2 translation cache. As more Java methods are JIT-compiled and executed, more translations are cached, leading to a continuous increase in these RWXP regions and, consequently, the process's RSS.

Architectural Solutions and Trade-offs

  • Use Native ARM64 Images (Recommended): Building or pulling Docker images specifically for `linux/arm64` eliminates the need for Rosetta 2 translation, preventing RWXP growth, improving performance, and reducing memory consumption. This is the optimal solution for M1 Mac development and deployment.
  • Deploy to x86-64 Infrastructure: If native ARM64 images are not feasible, deploying to x86-64 servers or cloud instances avoids the M1/Rosetta 2 specific issue.
  • Accept and Monitor (Not Recommended for Production): While not a bug, accepting continuous memory growth requires increasing container memory limits and potentially planning for periodic restarts. Disabling JIT (`-Xint`) is not a production solution due to severe performance degradation.

For system designers and developers, understanding this interaction is crucial for optimizing local development environments and ensuring consistent performance metrics between development and production environments, especially when transitioning to or from ARM-based architectures.

JavaDockerM1 MacRosetta 2JITMemory ManagementPerformanceARM64

Comments

Loading comments...