Что означает сброс потока локальной памяти в глобальную память? - PullRequest
10 голосов
/ 22 марта 2010

Мне известно, что цель изменчивых переменных в Java состоит в том, что записи в такие переменные сразу же видны другим потокам. Мне также известно, что одним из эффектов синхронизированного блока является сброс локальной памяти потока в глобальную память.

Я никогда полностью не понимал ссылки на «локальную нить» память в этом контексте. Я понимаю, что данные, которые существуют только в стеке, являются локальными для потоков, но когда я говорю об объектах в куче, мое понимание становится туманным.

Я надеялся, что получу комментарии по следующим пунктам:

  1. При выполнении на компьютере с несколькими процессорами, стирает ли локальная память потока просто ссылку на сброс кэша ЦП в ОЗУ?

  2. При выполнении на однопроцессорной машине это вообще что-нибудь значит?

  3. Если куча может иметь одну и ту же переменную в двух разных ячейках памяти (каждая из которых доступна из одного потока), при каких обстоятельствах это может возникнуть? Какие последствия это имеет для сбора мусора? Насколько агрессивно виртуальные машины делают подобные вещи?

  4. (РЕДАКТИРОВАТЬ: добавление вопроса 4) Какие данные сбрасываются при выходе из синхронизированного блока? Это все, что нить имеет локально? Разве только записи, которые были сделаны внутри синхронизированного блока?

    Object x = goGetXFromHeap(); // x.f is 1 here    
    Object y = goGetYFromHeap(); // y.f is 11 here
    Object z = goGetZFromHead(); // z.f is 111 here
    
    y.f = 12;
    
    synchronized(x)
    {
        x.f = 2;
        z.f = 112;
    }
    
    // will only x be flushed on exit of the block? 
    // will the update to y get flushed?
    // will the update to z get flushed?
    

В целом, я думаю, пытаюсь понять, означает ли локальный поток память, физически доступную только одному ЦП, или существует ли логическое разделение кучи локального потока, выполненное виртуальной машиной?

Любые ссылки на презентации или документацию были бы чрезвычайно полезны. Я потратил время на изучение этого, и хотя я нашел много хорошей литературы, я не смог удовлетворить свое любопытство в отношении различных ситуаций и определений локальной памяти потока.

Большое спасибо.

Ответы [ 4 ]

6 голосов
/ 23 марта 2010

Флеш, о котором вы говорите, известен как «барьер памяти». Это означает, что ЦП гарантирует, что то, что он видит в ОЗУ, также можно увидеть из других ЦП / ядер. Это подразумевает две вещи:

  • JIT-компилятор сбрасывает регистры процессора. Обычно код может хранить копию некоторых глобально видимых данных (например, содержимого поля экземпляра) в регистрах ЦП. Регистры не видны из других потоков. Таким образом, половина работы synchronized состоит в том, чтобы убедиться, что такой кэш не поддерживается.

  • Реализация synchronized также выполняет барьер памяти, чтобы гарантировать, что все изменения в оперативной памяти от текущего ядра распространяются на основную оперативную память (или что по крайней мере все другие ядра знают, что это ядро ​​имеет последнюю версию значения - протоколы когерентности кэша могут быть довольно сложными).

Вторая задача тривиальна для однопроцессорных систем (я имею в виду системы с одним процессором, который имеет одноядерный), но однопроцессорные системы в наши дни становятся все более редкими.

Что касается локальных куч, это теоретически может быть сделано, но обычно это не стоит усилий, потому что ничто не говорит о том, какие части памяти должны быть очищены с помощью synchronized. Это ограничение модели потоков с общей памятью: вся память должна быть разделена. При первом обнаружении synchronized JVM должна затем сбросить все свои «объекты кучи локального потока» в основное ОЗУ.

Тем не менее, недавняя JVM от Sun может выполнить «анализ побега», в котором JVM успешно доказывает, что некоторые экземпляры никогда не становятся видимыми из других потоков. Это типично, например, для StringBuilder экземпляров, созданных javac для обработки конкатенации строк. Если экземпляр никогда не передается в качестве параметра другим методам, он не становится «глобально видимым». Это делает его пригодным для локального распределения кучи потока или даже при правильных обстоятельствах для выделения на основе стека. Обратите внимание, что в этой ситуации нет дублирования; экземпляр не находится в "двух местах одновременно". Только JVM может хранить экземпляр в приватном месте, что не влечет за собой затрат на защиту памяти.

1 голос
/ 23 марта 2010

Одним из превосходных документов для выделения видов проблем является PDF-документ с технической сессии JavaOne 2009

Это не машина вашего отца фон Неймана: как современная архитектура влияет на ваши приложения Java

Клифф Клик, Azul Systems;Брайан Гетц, Sun Microsystems, Inc.

1 голос
/ 23 марта 2010

Это не так просто, как CPU-Cache-RAM. Это все в JVM и JIT, и они добавляют свое поведение.

Взгляните на Декларация "Двойная проверка заблокирована" . Это трактат о том, почему двойная проверка блокировки не работает, но он также объясняет некоторые нюансы модели памяти Java.

1 голос
/ 22 марта 2010

Это действительно деталь реализации, если текущее содержимое памяти объекта, который не синхронизирован, видно другому потоку.

Конечно, существуют ограничения в том, что вся память не хранится в дубликате, и не все инструкции переупорядочиваются, но дело в том, что базовая JVM имеет возможность, если она находит, что это более оптимизированный способ сделать это .

Дело в том, что куча действительно "правильно" хранится в основной памяти, но доступ к основной памяти медленнее по сравнению с доступом к кэшу ЦП или хранением значения в регистре внутри ЦП. Требуя, чтобы значение было записано в память (что и делает синхронизация, по крайней мере, когда снимается блокировка), оно вызывает запись в основную память. Если JVM может игнорировать это, она может повысить производительность.

С точки зрения того, что произойдет в системе с одним ЦП, несколько потоков могут сохранять значения в кэше или регистре даже при выполнении другого потока. Нет гарантии, что существует какой-либо сценарий, в котором значение является видимым для другого потока без синхронизации, хотя это, очевидно, более вероятно. Конечно, за пределами мобильных устройств однопроцессорный процесс идет по пути гибких дисков, так что это не будет долгое время актуальным.

Для дальнейшего чтения я рекомендую Java Concurrency на практике . Это действительно отличная практическая книга на эту тему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...