Теория и практика на корекция на модел Java памет Java, част 1

Какво е модел на паметта на Java (JMM), и какви проблеми са възникнали с нея първоначално?

Експертна група на JSR 133, който работи близо три години, наскоро издаде препоръки за това какво да правя с Java Memory Model Memory модел (JMM). В оригиналния JMM са били открити някои сериозни недостатъци, което води до невероятната сложност на семантиката на понятия, които трябваше да бъдат прости, като променлива. окончателно и синхронизирани. В това издание на Java теория и практика Brayan Getts показва как ще бъдат засилени семантиката на летлив и окончателно, да се извършат корекции в JMM на модела памет. Някои от тези промени вече са интегрирани в 1.4 пакет JDK; други чакат своя ред да бъдат включени в JDK 1.5.

Brayan Getts. Главен консултант, Quiotix

Платформата Java е интегрирал разделението в потоци и многопроцесорна на езика на много по-голяма степен, отколкото предишни програмни езици. Подкрепете този език платформено независим паралелизъм и многонишково беше амбициозни и иновативни решения, и може би затова не е изненадващо, че проблемът е малко по-сложно, отколкото Java архитекти първоначално мислеха. В основата на проблемите с синхронизация и темата безопасност са интуитивно неразбираеми тънкости на памет модел на Java (JMM), които първоначално са определени в глава 17 на Java Language Specification (Java Language Specification) и предефинира контролна група JSR 133.

Например, не всички системи за обработка на данни многопроцесорни подкрепа на кохерентността на кеша; ако един процесор има актуализирана стойност на променливата в кеш, но това все още не е възстановена до основната памет, други процесори, които не могат да видят новата стойност. При липса на две различни кохерентността на кеша на процесора видим за две различни стойности в една и съща памет клетката. Това може да звучи страшно, но той е специално замислен - е начин да се постигне по-голяма производителност и гъвкавост - но също така е източник на допълнителни усложнения за разработчици и компилатори, които генерират код, който използва тези функции.

Какво е модел на паметта, и защо ми е необходим?

Моделът на памет описва връзката между променливите в програмата (ниви случаи статични полета и масив елементи), както и данните за ниско ниво на съхранение на памет и възстановяване в реално изчислителна система. Обекти в крайна сметка, съхранени в паметта, но компилатор, по време на работа, процесора, или на кеш паметта може да се справи по-скоро безцеремонно инсталиране на времето за стойности, определени за променливи или спомен за тях роуминг. Например, компилаторът може да избере да се оптимизира цикъл брояч променлива, да бъдат пазени в регистър или кеш може да забави нова стойност за нулиране на променлива в основната памет на подходящо време. Всички тези оптимизации, насочени към повишаване на производителността и обикновено прозрачен за потребителя, но в многопроцесорни системи, понякога могат да се появят тези трудности.

Не пропускайте и другите статии от тази серия

JMM модел памет позволява на компилатора и кеш, а свободно се отнасят до реда, по който данните да се прехвърлят между кеша на процесора (или да се регистрирате) и основната памет, освен ако изрично е програмист пита за определени гаранции видимост използване синхронизирано или летлив. Това означава, че в отсъствието на операциите по памет синхронизация може да възникне в различен ред от гледна точка на различните потоци.

За разлика от Java, в C и C ++ езици като няма изрични паметта модели. програми С вместо да наследят модела памет на процесора изпълнение на програмата (въпреки че компилаторът за дадена архитектура може в действителност да знаете нещо за модел процесор, памет, както и част от отговорността за спазването пада на компилатора). Това означава, че паралелните програми в C могат да се извършват правилно с архитектура процесор, но няма да се движат по други процесорни архитектури. Въпреки модел памет JMM първоначално може да бъде объркващо, от това може да бъде значителна полза: програма, която е правилно синхронизирани в съответствие с JMM, трябва да работи правилно на всяка платформа, която поддържа Java.

Недостатъци на оригиналния модел Java памет

Докато JMM като модел на паметта, са определени в глава 17 от спецификацията Java Language Specification. е амбициозен опит да се определи модел на единна, по-платформа памет, тя има няколко едва доловими, но съществени недостатъци. Семантиката на синхронизирани и летлив е доста объркващо, толкова много, така че много добре обучени разработчиците понякога предпочитат да игнорират правилата, защото правописа правилно синхронизиран код при стария модел памет е трудно.

Старият модел на паметта JMM позволи някои изненадващи и противоречащи си неща, като появата на крайните полета не са ценностите, които са зададени в конструктора (което би могло да се превърне в един непостоянен неизменни обекти), както и неочаквани резултати, когато за преподреждане операции памет. Той също така предотвратява някои форми на програма за оптимизация време на компилация, които в някои случаи са ефективни. Ако сте прочели всички статии за проблема уверили заключване (виж ресурси.) Може би си спомняте как объркващо може да бъде операция за пренареждане и как неуловима, но сериозни проблеми могат да се промъкнат в кода си, когато не се синхронизира правилно (или активно се опитва да избегне синхронизация). По-лошо, много правилно синхронизирани програми работят добре в някои ситуации, като например, когато един малък товар върху един процесор системи, или процесори с по-силни паметта модели, отколкото е необходимо JMM.

Пренареждане термин, използван, за да опише няколко класа реално и очевидно преразпределение на операциите на паметта:

  • Компилаторът може да оптимизира толкова свободно, пренареждане конкретни инструкции, ако това не променя семантиката на програмата.
  • Процесорът е разрешено да извършват дейността си в ред при определени обстоятелства.
  • Cache обикновено са получили разрешение да извършват запис обратно към основните променливи памет не са в реда, в който са написани от програмата.

Всяко от тези условия може да доведе до факта, че от гледна точка на работата на друга тема не може да се случи в реда, както е посочено от програмата и независимо от източника преподреждане на модела памет всички считат за еквивалентни.

Цели JSR 133 експертна група

JSR 133 експертна група се събраха, за да се определи Java памет модел (JMM), има следните цели:

Отбележи, че разпокъсани процедури, като проверили заключване, се смачкват при новия модел на паметта. проверили "Корекция" заключване не е целта на усилията за създаване на нов модел на паметта. (Въпреки това, новите семантиката на летлив позволяват един от най-често предлаганите алтернативи, за да заключите проверили работата правилно, въпреки че използването на тази технология все още е желателно.)

След три години на активните дейности JSR 133 експертна група, стана ясно, че проблемът е много по-коварен от всеки мислеше. Такава е съдбата на всички пионери! Окончателните официални семантика е много по-сложно, отколкото се очакваше първоначално, а в действителност съвсем различен от този, който си представим, но неофициални семантиката е ясен и интуитивен. Тя ще бъде описан в част 2 на тази статия.

Синхронизация и видимост

Проблем № 1: Липса на неизменни обекти

Представете си, че вие ​​изпълнявате следния код:

s2 низ ще има отместване на 4 и дължина от 4, но ще използва същия характер масив, съдържащ "/ ЮЕсАр / ПТУ". заедно с S1. Преди да стартирате String строителя. конструктор на обекта, за да нулирате всички области, включително дължината на крайния поле и офсет, стойностите по подразбиране. В началото String дизайнер в тези области са определени желаната стойност. Но по силата на стария модел на паметта при липса на синхронизация е възможно някои нишка ще бъде временно компенсиране виж областта с по подразбиране стойност 0, и едва по-късно видя истинската стойност на 4. В резултат на това стойността на s2 се променя от "/ ЮЕсАр" за "/ ПТУ". Това не е това, което е трябвало, а може би няма да работи на всички платформи, или JVM, но се допуска спецификацията на паметта на стария модел.

Проблем № 2: Пренареждане нестабилна и не енергонезависима памет

Друга важна област, в която текущата памет модел JMM доведе до някои много объркващи резултати бяха преподреждане на операциите на паметта в летливи полета. Съществуващият модел на паметта JMM казва, че четенето и писането летлив трябва да бъде изпратено директно до основната памет и забранява кеширане стойности в регистрите и заобикаляйки кеша процесори. Това позволява множество нишки винаги видите последната стойността на променливата. Въпреки това, се оказа, че това определение не е летлив толкова полезни, колкото първия очакваното, което доведе до значително объркване с действителното местоназначение летлив.

За да се осигури по-добра производителност при липса на синхронизация, компилатор и кеш по време на работа обикновено е позволено да преподредите обикновената операция на паметта, толкова дълго, колкото в момента изпълнява нишката не може да направи разлика. (Това се нарича в рамките на конци като-ако-сериен семантика - един на kvaziposledovatelnaya семантика). Четене и писане на летлив, обаче, напълно разпределени в определен ред на потока; съставител и кеш, не може да преразпределя помежду си, за да четат и пишат. За съжаление модел памет JMM не е позволено да се преразпредели четене и записване сравнително волатилна четат и пишат обикновени променливи. Това означава, че не можем да използваме тагове (знамена) летливи като индикатори за какви операции са завършени. Да разгледаме следния код, в който идеята е, че полето на летлив инициализира (инициализира), следва да се посочи, че подготовката за работа е завършена:

Обява 1. С помощта на летливи поле като променлива "заключване".

Идеята тук е, че летливи инициализира променливата действа като заключване, за да покаже, че набор от други операции е завършена. Това е добра идея, но в рамките на стария модел памет Java, той не работи, тъй като този модел позволява операцията за запис не е енергонезависима (например влизане в configOptions. Както и влизането в поле карта. Цитирани configOptions) се преразпределя с летливи напишете операции. Ето защо, друга тема да видите инициализира като вярно, но все още да не е еднакъв или текущия изглед на полеви configOptions или обекти, за които се отнася. Старите семантиката на летлив обеща достъп само за четене или писане на променлива, и не е имало обещания за други променливи. Въпреки, че този подход е по-лесно да се прилагат ефективно, той се оказа по-малко благоприятен, отколкото първоначалната идея.

Както е определено в глава 17 от спецификацията на Java език спецификация. модел памет JMM има някои сериозни пропуски, които позволяват на някои интуитивно неразбираеми и нежелани неща се случват с програми, които изглеждат доста разумни. Ако тя е прекалено трудно да се напише паралелни класове правилно, много паралелни класове са гарантирани за да не работи както се очаква, и това е липсата на платформа. За щастие, това е възможно да се създаде модел на паметта, която ще бъде по-съвместимо с интуицията на повечето разработчици и в същото време да не е в нарушение със код, който е бил правилно синхронизиран при стария модел памет. работа JSR 133 експертна група бе насочена точно в това. Следващия месец ще разгледаме детайлите на новия модел на паметта (повечето от които вече са били построени в JDK 1.4).