2016-10-21 6 views
0

Насколько я знаю, окончательный говорит CPU о том, что он может кэшировать переменную/объект (он в любом случае отличается от CPU к процессору. Я думаю, что x86 CPU-серверы действительно делают основной кеш L1) Мой вопрос is:окончание и утечка памяти

В следующем примере у меня есть объект myObjectFinal, установленный как final, поэтому процессор может кэшировать его, а затем он изменяет значение внутри. Означает ли это, что myObject также не может быть изменен?

Описана ли ссылка, если я устанавливаю финал, и CPU решает его кешировать?

Что делать, если я изменяю объект myObject, гарантируется, что тайник в кэше также будет изменен?

// thread 1 
volatile MyObject myObject = new MyObject(); 
// thread 2 
final MyObject myObjectFinal = myObject; 
myObjectFinal.setData(1); 
// thread 1 
myObject.setData(2); 

Возможно, код не говорит точно, что я имею в виду. Итак, главный вопрос заключается в следующем: делает ли измененный объект окончательным или изменчивым какие-либо последствия для того, как объект кэшируется процессором. Если это произойдет, когда я должен использовать final или volatile для изменяемых объектов?

Имеет ли CPU когда-либо кешируемый объект или final/volatile, абсолютно не влияет на них и используется только для согласованности кодирования?

+0

Ваш вопрос скорее о ключевом слове 'volatile', чем о' final'. Если ваша переменная (member) объявлена ​​'volatile', JVM будет считать, что изменения в других потоках видны в текущем потоке. без ключевого слова 'volatile' это не будет, без ключевого слова' final'. –

+1

** У меня есть объект myObjectFinal, установленный как final ** - Нет, у вас нет 'Object', установленного как' final', у вас есть ** Object Reference ** установлен как 'final'. Как правило, 'volatile' и' final' имеют смысл для видимости примитивных типов и других сценариев, требуют явной «синхронизации». –

+0

@Sabir Очевидно, что я помещаю окончательный вариант в ссылку на объект! Мой вопрос не имеет смысла, если я буду копировать объект или использовать примитивы. Ваш комментарий не помогает вообще. И снова Синхронизация отличается от изменчивости. Этот вопрос касается кэширования в CPU, не делающего часть кода атомарной. – chris

ответ

0

final означает, что после инициализации значение не может быть изменено.

После того, как была назначена окончательная переменная, она всегда содержит одно и то же значение. Если конечная переменная содержит ссылку на объект, то состояние объекта может быть изменено операциями над объектом, но переменная всегда будет ссылаться на один и тот же объект. https://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.4

так что если вы не переназначить myObjectFinal, ничего плохого не случится,

Однако если изменить один и тот же объект из нескольких потоков, то потребуется синхронизации. (не использовать volatile)

Прежде всего, избавитесь от объектной ориентации. Объекты - это виртуальная конструкция. мы говорим о низкоуровневых материалах, таких как регистры и кеши, - есть только примитивы (каждый класс может быть сведен к набору примитивов)

Причина, по которой нам нужны кеши, проста. Память просто ужасна медленно. Таким образом, идея состоит в том, чтобы сократить количество обращений к памяти до минимума.

рассмотрим следующий код:

1. x=0; 
2. if (x == 1) 
3. x++ 

внутри обычного компьютера происходит сейчас что-то вроде этого (без регистра-оптимизации)

1. write 0 into a register 
    write the register into the memory 
2. read x from the memory into a CPU-register 
    compare it with 1 and go to 3 or 4 
3. read x from the memory into a CPU-register 
    increment register 
    write the register into the memory 

много избыточных операций памяти ... но вы могут их устранить

1. write 0 into a register 
    write the register into the memory 
2. compare the register with 1 and go to 3 or 4 
3. increment register 
    write the register into the memory 

Мы храним значение x в регистре стер. Так почему бы не сохранить все переменные в реестрах? в первую очередь, существует множество доступных регистров, поэтому вы не можете хранить все там. Это скорее временная вещь для хранения переменной в регистре.(например, внутри области) Другая проблема заключается в том, что никакое другое ядро ​​и/или устройство не могут считывать из регистров. то вам нужно перезагрузить/записать переменную.

Если вы объявите переменную окончательной, она никогда не будет изменена. Таким образом, переменная может храниться в регистре без проблем с несогласованностью. Однако это не означает, что другие потоки не могут получить доступ к переменной, поскольку она также хранится внутри памяти. Он гарантирует, что значение в памяти всегда действует. Но все переменные, независимо от того, являются ли они окончательными или нет, могут и будут храниться в течение короткого времени в регистрах. поэтому нам нужно использовать инструмент синхронизации, который заставляет обратную запись из регистров в память, если вы хотите гарантировать согласованность нескольких потоков/ядер.

однако реальный кэш-память процессора - это совершенно другая тема. его полная прозрачная система (выполняется на стороне аппаратного обеспечения!), на которую влияет только шаблон доступа к памяти (если только вы действительно не делаете сумасшедший низкоуровневый уровень в сборке). Чтобы сделать это коротко: CPU-cache в основном кэширует каждый байт, если вы явно не заставляете его не делать этого.

+0

Поскольку вы уже ссылаетесь на JLS, вот ссылка: [JLS 17.5 окончательная полевая семантика] (https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5). Конечное поле абсолютно влияет на кэширование ЦП, в JLS явно упоминается, что JVM может решить кэшировать конечное значение поля в регистре CPU. –

+0

Да, регистры, но он явно упоминал кеш-память L1, который не должен быть затронут. – Domso

+0

Реестр CPU. Я думаю, что еще более недоступен для других потоков/ядер, чем L1 при рассмотрении утечек памяти. Вопрос в том, что утечки памяти не там, где точно кэшируется – chris

Смежные вопросы