Да, все правильно.
Полностью соптимизированный код статистику более не собирает и, если вдруг посреди исполнения программы профиль резко поменяется, столкнемся с деградацией производительности.
Это в том случае, если метод все время продолжает исполняться.
Метод, который какое-то время не исполняется, может быть выкинут из CodeCache во время GC и при последующих вызовах будет компилироваться заново.
Девиртуализация работает не так. Учитывается динамический профиль конкретного сценария. Может быть хоть 100 реализаций виртуального метода, но если в конкретном месте конкретного приложения используется преимущественно одна и или две реализации, то данный вызов все равно будет девиртуализован и заинлайнен.
Про SimpleDateFormat и согласен, и несогласен одновременно.
Действительно, это тяжелый метод, который на практике в нашем случае сжирал существенную долю CPU высоконагруженного веб-сервера.
Но я не соглашусь с тем, что виной тому создание нового SimpleDateFormat каждый раз. Медленным здесь является сам процесс конвертации в строку заданного формата. Я эту проблему решил, написав свой конвертер HttpDate, заточенный под конкретный формат, фигурирующий в HTTP протоколе.
Если по-длиннее, Jazelle бывает разная. Первоначально так называлась попытка создать JVM в аппаратном исполнении. Отсюда и приставка DBX — Direct Bytecode Execution (а не Dynamic, как написано в русской Википедии). Однако спецификация JVM слишком сложна для того, чтобы ее полностью реализовать в железе. Поэтому Jazelle DBX предусматривает аппаратное исполнение лишь простых байткодов, а сложные отдает на откуп софту. Раньше эта технология позволяла ускорить интерпретацию Java-байткода до 50%, однако как раз с появлением CLDC HI она безнадежно устарела. Несмотря на то, что Jazelle DBX поддерживается в CLDC HI, толк от нее есть только в interpreter-only режиме. При включенной JIT-компиляции DBX только снижает общую производительность.
Jazelle RCT (Runtime Compiler Environment) — совершенно иная технология, не имеющая с DBX ничего общего. RCT — это специальный режим некоторых ARM v7 процессоров, расширяющий Thumb-2 несколькими новыми инструкциями с целью порождения JIT-компиляторами виртуальных машин более компактного и эффективного кода. Примечательно, что в CLDC HI поддержка RCT появилась гораздо раньше, чем само железо. Для реализации использовалась предварительная спецификация от ARM и специально разработанный собственный эмулятор. С выпуском реальных чипов с поддержкой Jazelle RCT, технология сразу заработала в CLDC HI с минимальными изменениями.
OK, согласен: Object[] лучше заменить на AtomicReference.
А насчет «код некорректен для написания кем угодно» у меня есть свое мнение: код имеет право на существование до тех пор, пока он решает конкретную задачу в конкретном случае. Мартин Томпсон, которого ты часто цитируешь, тому в пример. Скажем, в многопоточной программе, написанной согласно Single Writer Principle, можно добиться огромного прироста производительности, заменив volatile store на обычный store. Программа будет абсолютно правильно работать на x86 архитектуре, хотя, согласно JMM, будет некорректной.
И вообще, из всех предложенных вариантов, ИМХО, самый «элегантный» — с Exchanger.
Согласен :)
А я не совсем :) Exchanger служит для двустороннего обмена, а в данном примере имеет место односторонний (producer-consumer). ИМХО лучше всего подходит SynchronousQueue. И по длине кода столько же: один exchange меняется на put, а второй — на take.
The park method may also return at any other time, for «no reason»
На практике этого никогда не произойдет, т.к. HotSpot фильтрует «случайные» пробуждения.
Но synchronized в любом случае не нужен. В том-то и «фишка» LockSupport: park() можно вызывать даже после того, как случился unpark() — дедлока не будет.
Все синхронизационные примитивы java.util.concurrent (ReentrantLock, Semaphore, ArrayBlockingQueue, Exchanger и т.д.) реализованы на основе LockSupport.
Делать wait / notify на BlockingQueue — это оскорбление всего Java Concurrency.
BlockingQueue на то и blocking, что вызов take() будет ждать, пока в очередь не поступит элемент из другого потока.
… которая вылетает с ошибкой (segfault). Обычно студенты в таких ситуациях используют деление на ноль.
Если понимать буквально, то деление на ноль вызывает вовсе не segmentation fault, а SIGFPE.
Сегфолты связаны с ошибками обращения к памяти, и main=0 — как раз такой случай.
Если уж и клиент, и агент все равно отдельные самописные, то смысл использовать JMX/RMI? Стандартный RMI тяжеловесный и медленный с побочными эффектами типа периодического Full GC. Клиент на Java — тоже не лучший выбор, особенно, когда мониторинг дергается очень часто.
У нас, например, агент отдает статистику plain text'ом по HTTP, а Cacti забирает ее просто curl'ом.
Именно ГСЧ и используется в HotSpot для вычисления hashCode в первый раз на объекте (см. synchronizer.cpp, стр. 537).
А какой бы вы предложили вариант для вычисления identityHashCode без ГСЧ?
Популярное заблуждение — использовать адрес объекта — не подходит по ряду причин: адрес объекта изменяется во время GC, кроме того, выделяемые подряд объекты будут иметь последовательные хеш-коды.
Заминусовали очень правильный комментарий!
Если Dalvik VM в качестве Object.hashCode() использует адрес объекта, то всем известный HotSpot совершенно точно для первого вычисления hashCode объекта использует random (см. стр. 537). Далее полученное число сохраняется в заголовке объекта для последующих вызовов hashCode.
Мы, значит, делаем все возможное, чтобы по-максимуму использовать Java-решения, а вы нам тут C настоятельно советуете?.. Шучу, ничего не имею против. За Varnish спасибо. Впрочем, любое стандартное решение в чистом виде нам вряд ли подойдет, так как в любом случае нужна некоторая портало-специфичная логика, например, фильтрация по HTTP-заголовкам, проверка security-токенов в запросах и т.п. Malloc в чистом виде опять же не подойдет — т.к. при рестарте все теряется.
На самом деле, медленность Java по сравнению с C сильно преувеличивается. И, в частности, этой статьей я хотел показать, что на Java можно писать высокопроизводительные приложения, не уступающие программам на C.
Полностью соптимизированный код статистику более не собирает и, если вдруг посреди исполнения программы профиль резко поменяется, столкнемся с деградацией производительности.
Это в том случае, если метод все время продолжает исполняться.
Метод, который какое-то время не исполняется, может быть выкинут из CodeCache во время GC и при последующих вызовах будет компилироваться заново.
Действительно, это тяжелый метод, который на практике в нашем случае сжирал существенную долю CPU высоконагруженного веб-сервера.
Но я не соглашусь с тем, что виной тому создание нового SimpleDateFormat каждый раз. Медленным здесь является сам процесс конвертации в строку заданного формата. Я эту проблему решил, написав свой конвертер HttpDate, заточенный под конкретный формат, фигурирующий в HTTP протоколе.
Если по-длиннее, Jazelle бывает разная. Первоначально так называлась попытка создать JVM в аппаратном исполнении. Отсюда и приставка DBX — Direct Bytecode Execution (а не Dynamic, как написано в русской Википедии). Однако спецификация JVM слишком сложна для того, чтобы ее полностью реализовать в железе. Поэтому Jazelle DBX предусматривает аппаратное исполнение лишь простых байткодов, а сложные отдает на откуп софту. Раньше эта технология позволяла ускорить интерпретацию Java-байткода до 50%, однако как раз с появлением CLDC HI она безнадежно устарела. Несмотря на то, что Jazelle DBX поддерживается в CLDC HI, толк от нее есть только в interpreter-only режиме. При включенной JIT-компиляции DBX только снижает общую производительность.
Jazelle RCT (Runtime Compiler Environment) — совершенно иная технология, не имеющая с DBX ничего общего. RCT — это специальный режим некоторых ARM v7 процессоров, расширяющий Thumb-2 несколькими новыми инструкциями с целью порождения JIT-компиляторами виртуальных машин более компактного и эффективного кода. Примечательно, что в CLDC HI поддержка RCT появилась гораздо раньше, чем само железо. Для реализации использовалась предварительная спецификация от ARM и специально разработанный собственный эмулятор. С выпуском реальных чипов с поддержкой Jazelle RCT, технология сразу заработала в CLDC HI с минимальными изменениями.
А насчет «код некорректен для написания кем угодно» у меня есть свое мнение: код имеет право на существование до тех пор, пока он решает конкретную задачу в конкретном случае. Мартин Томпсон, которого ты часто цитируешь, тому в пример. Скажем, в многопоточной программе, написанной согласно Single Writer Principle, можно добиться огромного прироста производительности, заменив volatile store на обычный store. Программа будет абсолютно правильно работать на x86 архитектуре, хотя, согласно JMM, будет некорректной.
А сам бы я реализовал, как уже сказал, на SynchronousQueue.
Но synchronized в любом случае не нужен. В том-то и «фишка» LockSupport: park() можно вызывать даже после того, как случился unpark() — дедлока не будет.
Все синхронизационные примитивы java.util.concurrent (ReentrantLock, Semaphore, ArrayBlockingQueue, Exchanger и т.д.) реализованы на основе LockSupport.
BlockingQueue на то и blocking, что вызов take() будет ждать, пока в очередь не поступит элемент из другого потока.
Сегфолты связаны с ошибками обращения к памяти, и main=0 — как раз такой случай.
У нас, например, агент отдает статистику plain text'ом по HTTP, а Cacti забирает ее просто curl'ом.
hg.openjdk.java.net/jdk7/jdk7/hotspot/file/tip/src/share/vm/runtime/synchronizer.cpp, стр. 537.
Этот код выполняется при вызове Object.hashCode() и System.identityHashCode() в HotSpot JVM.
А какой бы вы предложили вариант для вычисления identityHashCode без ГСЧ?
Популярное заблуждение — использовать адрес объекта — не подходит по ряду причин: адрес объекта изменяется во время GC, кроме того, выделяемые подряд объекты будут иметь последовательные хеш-коды.
Если Dalvik VM в качестве Object.hashCode() использует адрес объекта, то всем известный HotSpot совершенно точно для первого вычисления hashCode объекта использует random (см. стр. 537). Далее полученное число сохраняется в заголовке объекта для последующих вызовов hashCode.
На самом деле, медленность Java по сравнению с C сильно преувеличивается. И, в частности, этой статьей я хотел показать, что на Java можно писать высокопроизводительные приложения, не уступающие программам на C.