Что сбрасывание распараллеливает локальную память к средней глобальной памяти?

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

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

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

  1. Когда выполнение на машине с несколькими процессорами, сбрасывание локальной памяти потока просто относятся к сбрасыванию кэша ЦП в RAM?

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

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

  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?
    

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

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

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

10
задан Jack Griffith 22 March 2010 в 21:13
поделиться

4 ответа

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

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

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

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

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

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

6
ответ дан 4 December 2019 в 01:56
поделиться

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

This Is Not Your Father's Von Neumann Machine: How Modern Architecture Impacts Your Java Apps

By Cliff Click, Azul Systems; Brian Goetz, Sun Microsystems, Inc.

1
ответ дан 4 December 2019 в 01:56
поделиться

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

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

1
ответ дан 4 December 2019 в 01:56
поделиться

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

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

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

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

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

1
ответ дан 4 December 2019 в 01:56
поделиться
Другие вопросы по тегам:

Похожие вопросы: