← Concepts

阅读中文版本

Language Memory Models

A language memory model defines which values multithreaded reads may legally see, balancing compiler optimization freedom against the guarantees programs need for synchronization.

runtime

Definition

A memory model is the contract between the language specification and concurrent programs: it defines permitted observable behaviors of reads and writes across threads. Optimizations — reordering, caching, elimination — are allowed only when the permitted values remain legal. Data-race-free programs receive strong guarantees; programs with data races get weak or undefined ones.

Context and scope

Java Memory Model chapter 17, C++ and Rust memory order, C++11 model, Go happens-before. Determines whether lock-free code is portable across hardware and compiler versions.

Common misconceptions

  • Hardware defines the memory model — compilers reorder too; the language model sits above both and is what your code actually relies on
  • Data races sometimes produce stale values only — in C++ and Rust a data race is undefined behavior; the compiler may exploit its absence anywhere
  • Atomics make code correct — atomics with the right memory ordering make specific patterns correct; wrong ordering silently breaks on other architectures

Anti-patterns

  • Reasoning about interleavings only — reorderings and store buffering produce outcomes no sequential interleaving explains
  • Publishing shared data with plain writes guarded by nothing and working by luck on x86
  • Weakening memory orderings in hot paths based on one architecture benchmarks and shipping ARM-only corruption

Related concepts

Authoritative source

https://docs.oracle.com/javase/specs/jls/se21/html/jls-17.html

What it is

A specification must let compilers reorder and cache aggressively while giving programmers synchronization tools that mean something. The memory model is the reconciliation: a precise list of legal outcomes for every concurrent program, and requirements for the operations — locks, atomics, volatiles — that shrink that list.

How it works

Happens-before relations connect synchronization actions: unlock happens-before subsequent lock, volatile write happens-before its read, thread start before its first action. Reads must see the last write ordered before them or any racing write — and races carry the severity the language assigns, from defined-but-weak to undefined.

When to study it

Writing or reviewing lock-free code, interpreting atomic memory order parameters, and debugging bugs that only reproduce on ARM. Framework authors document their guarantees in these terms.

Pitfalls

Compilers exploit race-freedom assumptions globally: one racy read can poison optimizations far from the race. Publication patterns are the practical application — building a data structure then storing its pointer with release semantics — and getting the fence placement wrong is the practical failure.