Модель пам'яті Java (, JMM) описує, як потоки в Java взаємодіють через пам'ять. Разом з описом виконання коду в однопотоковому режимі модель пам'яті визначає семантику мови програмування Java.

Початкова модель пам'яті Java, розроблена у 1995 році, широко вважалася недосконалою, оскільки вона перешкоджала багатьом оптимізаціям під час виконання програм і не забезпечувала достатньо сильних гарантій безпечності коду. Її було переглянуто в межах Java Community Process як Java Specification Request 133 (JSR-133), що набула чинності у 2004 році для Tiger (Java 5.0).

Контекст Мова програмування Java та її платформа забезпечують підтримку потоків. Синхронізація між потоками є добре відомою складною проблемою для розробників; ця складність додатково зростає через те, що застосунки Java можуть виконуватися на широкому спектрі процесорів і операційних систем. Щоб мати змогу робити висновки щодо поведінки програми, розробники Java вирішили чітко визначити можливу поведінку всіх програм Java.

На сучасних платформах код часто виконується не в тому порядку, в якому його було написано. Компілятор, процесор і підсистема пам'яті змінюють порядок виконання інструкцій для досягнення максимальної продуктивності. На багатопроцесорних архітектурах окремі процесори можуть мати власні локальні кеші, які не синхронізовані з основною пам'яттю. Загалом небажано вимагати, щоб потоки постійно залишалися повністю синхронізованими між собою, оскільки це було б надто витратним з погляду продуктивності. Унаслідок цього різні потоки в будь-який момент часу можуть спостерігати різні значення тих самих спільних даних.

В однопотоковому середовищі міркувати про виконання коду досить просто. Типовий підхід вимагає, щоб система реалізовувала семантику as-if-serial («ніби послідовного виконання») для кожного окремого потоку. Коли окремий потік виконується, це має виглядати так, ніби всі його дії відбуваються в тому порядку, у якому вони записані в програмі, навіть якщо фактично вони виконуються в іншому порядку.

Якщо один потік виконує свої інструкції в іншому порядку, інший потік може спостерігати цей змінений порядок виконання, навіть якщо це не впливає на семантику першого потоку. Наприклад, розглянемо два потоки, які одночасно виконують такі інструкції, причому змінні `x` і `y` спочатку ініціалізовані значенням 0:

Thread 1 Thread 2 x = 1; int r1 = y; y = 2; int r2 = x;

Якщо перестановка інструкцій не виконується і читання `y` у потоці 2 повертає значення 2, то наступне читання `x` має повернути значення 1, оскільки запис у `x` було виконано перед записом у `y`. Проте якщо два записи буде переставлено місцями, тоді читання `y` може повернути значення 2, а читання `x` — значення 0.

Модель пам'яті Java (JMM) визначає допустиму поведінку багатопотокових програм і, відповідно, описує, коли такі перестановки можливі. Вона накладає обмеження на взаємодію між потоками та основною пам'яттю під час виконання програми, щоб забезпечити узгоджену та надійну роботу застосунків Java. Завдяки цьому стає можливим аналізувати виконання коду в багатопотоковому середовищі навіть за наявності оптимізацій, які виконують динамічний компілятор, процесор(и) та кеші.

Модель пам'яті

Для виконання одного потоку правила є простими. Специфікація мови Java вимагає, щоб віртуальна машина Java дотримувалася семантики within-thread as-if-serial («ніби послідовного виконання в межах потоку»). Середовище виконання (під яким у цьому випадку зазвичай мають на увазі динамічний компілятор, процесор і підсистему пам'яті) може виконувати будь-які корисні оптимізації, якщо гарантовано, що результат виконання ізольованого потоку буде точно таким самим, як і за умови виконання всіх операторів у тому порядку, у якому вони записані в програмі (цей порядок також називають порядком програми; ).

Основне застереження полягає в тому, що семантика as-if-serial не гарантує однакового бачення даних різними потоками. Модель пам'яті чітко визначає, які значення можуть бути повернуті під час читання даних. Основні правила дозволяють переставляти окремі дії, якщо це не порушує семантику as-if-serial для потоку. Водночас дії, що забезпечують взаємодію між потоками, такі як захоплення або звільнення блокування, гарантують, що всі дії, які відбулися до них, будуть видимими для інших потоків, які спостерігають наслідки цих дій. Наприклад, усе, що відбулося до звільнення блокування, вважатиметься таким, що сталося раніше, і буде видимим для всього, що відбувається після наступного захоплення того самого блокування.

Математично всі дії, виконані програмою, утворюють частковий порядок, який називають порядком happens-before. Порядок happens-before включає порядок програми: якщо одна дія передує іншій у порядку програми, то вона також передує їй у порядку happens-before. Крім того, операції звільнення блокування та його подальшого захоплення утворюють ребра в графі happens-before. Операція читання може повернути значення, записане певною операцією запису, якщо цей запис є останнім записом цієї змінної перед читанням уздовж деякого шляху в порядку happens-before, або якщо цей запис не впорядкований відносно цього читання в порядку happens-before.

Вплив

Модель пам'яті Java була першою спробою створити всеохопну модель пам'яті для популярної мови програмування. Її появу зумовило дедалі ширше поширення паралельних і конкурентних систем, а також потреба забезпечити інструменти й технології з чітко визначеною семантикою для таких систем. Відтоді необхідність моделі пам'яті стала загальновизнаною, і подібну семантику було впроваджено в інших мовах, зокрема в C++.

Див. також Модель пам'яті Паралелізм в Java

Примітки

Посилання Java theory and practice: Fixing the Java Memory Model, part 1 - An article describing problems with the original Java memory model. Java theory and practice: Fixing the Java Memory Model, part 2 - Explains the changes JSR 133 made to the Java memory model. Java Memory Model Pragmatics (transcript) The Java Memory Model links Java internal structure

JSR-133 webpage JSR-133 FAQ JSR-133 implementation guide