Поймать этот баг в продакшне ещё постараться надо. Опять же, нам «повезло» лишь благодаря высоким нагрузкам: сервер только поднимается, а на него сразу тысяча одновременных запросов прилетает с участием разных классов: RegularImmutableList, SingletonImmutableList и т.д., и все они начинают конкурентно инициализироваться. Много ли где такое встречается?
Как вариант ещё Unsafe.allocateInstance.
Тоже не вызывает конструктор, причём безо всякой кодогенерации.
Является JVM интринсиком, то есть, инлайнится на «ура».
Как сказать…
Вот, например, до сих пор неисправленный: JDK-8043740
Еще один относительно свежий: JDK-7019078
Ну и, конечно, печально знаменитая уязвимость.
Если бы было всё так просто! Первый же Exception сломает весь пул.
Чтобы всё работало правильно, придётся делать обёртки над Connection и Statement, которые ловят исключения и инвалидируют (закрывают) Connection, не кладя его обратно в пул. Вдобавок нужно ограничивать размер пула, а новые соединения открывать вне synchronized, чтобы избежать нежелательных задержек в многопоточном приложении.
Совсем разные вещи.
Интринсики — часть JVM, их пишут компиляторщики. По сути, это переписывание тела метода на суровом хотспотовском IR.
JavaCritical — оптимизация для библиотек. Чтобы код на C, написанный не JVM разработчиками, эффективней вызывался.
Посмотрите, как выглядит хоть один интринсик, например, LibraryCallKit::string_indexOf, и всё станет сразу ясно.
Для native методов JIT-компилятор генерирует обёртки. Т.е. вся описанная процедура (создание фрейма, перекладывание аргументов, проверка safepoint и т.д.) происходит в динамически скомпилированном коде под данный конкретный метод. Но непосредственно с нативной реализацией метода, которая уже и так в бинарном виде, JIT, естественно, ничего сделать не может и просто вызывает, как есть.
Причины есть. Раз уж даже консервативные разработчики JVM это реализовали.
Не всё можно сделать в Java. Например, вы никак не заставите JIT-компилятор использовать SIMD. Но при этом и native не будет выигрывать, поскольку вся выгода уйдет на JNI-обертки.
— Не поддерживаются TCP опции: TCP_DEFER_ACCEPT, TCP_CORK и т.п.
— Не поддерживаются флаги send и recv: MSG_MORE, MSG_PEEK…
— Selector глючный и не потокобезопасный.
— Нельзя делеать select() на блокирующих сокетах.
— setSoTimeout не работает на SocketChannel'ах.
Да и по производительности можно выжать больше.
Меня больше интересует сколько времени эти вызовы занимают по сравнению со временем работы самого алгоритма.
Так из графиков же видно. Сравните arrayElementsCritical (стандартный JNI) с javaCritical: делают они одно и то же, значит, вся разница — и есть накладные расходы. Собственно, абзац после графиков отвечает на вопрос.
Это сколько?
Зависит от приложения. Для одних паузы в 100 мс критичны, другие и 5 секунд могут подождать.
Скажем так: если в 10 мс укладывается, значит, для большинства случаев сгодится.
Если есть только JavaCritical_myMethod, но нет Java_myMethod, бросится UnsatisfiedLinkError. Функция со стандартной сигнатурой нужна всегда. Для удобства она может просто распаковывать аргументы и передавать их уже в JavaCritical_ реализацию.
Не, этим только проверяется, что мы не пытаемся изменить состояние чужого потока.
Но любой поток может сам заходить в jni_critical (Thread::current() == this).
А я вот тут наткнулся на то, что Даг Ли писал про 100 микросекунд.
Хотя оценка, данная Сергеем, мне кажется более реалистичной.
Все ошибаются, ничего позорного в этом нет. Коллегам из Google, на самом деле, респект, что отнеслись с пониманием и оперативно исправили.
Стандартная сериализация 1) медленная, 2) неэкономная, 3) не справляется с эволюцией классов.
Тоже не вызывает конструктор, причём безо всякой кодогенерации.
Является JVM интринсиком, то есть, инлайнится на «ура».
И на StackOverflow было.
Код, который нормально работал на Java 6, может внезапно начать падать на Java 7 c
в связи с тем, что в Java 7 поменялся алгоритм, лежащий в основе Arrays.sort / Collections.sort.
Вот, например, до сих пор неисправленный: JDK-8043740
Еще один относительно свежий: JDK-7019078
Ну и, конечно, печально знаменитая уязвимость.
Exceptionсломает весь пул.Чтобы всё работало правильно, придётся делать обёртки над
ConnectionиStatement, которые ловят исключения и инвалидируют (закрывают)Connection, не кладя его обратно в пул. Вдобавок нужно ограничивать размер пула, а новые соединения открывать внеsynchronized, чтобы избежать нежелательных задержек в многопоточном приложении.Нет там Reflection в рантайме. Динамическая генерация байткода + Instrumentation.retransformClasses
Интринсики — часть JVM, их пишут компиляторщики. По сути, это переписывание тела метода на суровом хотспотовском IR.
JavaCritical — оптимизация для библиотек. Чтобы код на C, написанный не JVM разработчиками, эффективней вызывался.
Посмотрите, как выглядит хоть один интринсик, например, LibraryCallKit::string_indexOf, и всё станет сразу ясно.
Не всё можно сделать в Java. Например, вы никак не заставите JIT-компилятор использовать SIMD. Но при этом и native не будет выигрывать, поскольку вся выгода уйдет на JNI-обертки.
— Не поддерживаются флаги send и recv: MSG_MORE, MSG_PEEK…
— Selector глючный и не потокобезопасный.
— Нельзя делеать select() на блокирующих сокетах.
— setSoTimeout не работает на SocketChannel'ах.
Да и по производительности можно выжать больше.
Зависит от приложения. Для одних паузы в 100 мс критичны, другие и 5 секунд могут подождать.
Скажем так: если в 10 мс укладывается, значит, для большинства случаев сгодится.
Есть планы опробовать на нашем Java сервере, где всё сетевое I/O на native методах.
JavaCritical_myMethod, но нетJava_myMethod, броситсяUnsatisfiedLinkError. Функция со стандартной сигнатурой нужна всегда. Для удобства она может просто распаковывать аргументы и передавать их уже вJavaCritical_реализацию.Но любой поток может сам заходить в
jni_critical(Thread::current() == this).