Cleaners are a lightweight and more robust alternative to finalization. They are lightweight because they are not created by the VM and thus do not require a JNI upcall to be created, and because their cleanup code is invoked directly by the reference-handler thread rather than by the finalizer thread. They are more robust because they use phantom references, the weakest type of reference object, thereby avoiding the nasty ordering problems inherent to finalization.
…
Cleaners may also be invoked directly; they are thread safe and ensure that they run their thunks at most once.
В Java есть понятия политики безопасности и пермиссий. Когда вы запускаете java локально, SecurityManager выключен, поэтому вам и позволены все эти хаки. Теперь попробуйте запустить java с параметром -Djava.security.manager и тут же получите java.security.AccessControlException: access denied (java.lang.RuntimePermission accessClassInPackage.sun.misc). Java — безопасная платформа (для аплетов, например, SecurityManager всегда включен), но она не ограничивает возможности тех, кто знает, что делает.
Есть простой, честный и потокобезопасный способ написать exceptions на макросах в Си, хоть и не столь элегантный, как в статье. Именно такой метод используется в HotSpot JVM:
#define TRAPS Thread* _thread
#define CHECK_(x) _thread); if (_thread->has_exception()) return x; (0
#define CHECK_0 CHECK_(0)
#define THROW_(msg, x) { _thread->set_exception(msg, __FILE__, __LINE__); return x; }
#define THROW_0(msg) THROW_(msg, 0)
#define CATCH(ex) for (Exception* ex = _thread->exception(); ex != NULL && _thread->clear_exception(); ex = NULL)
int this_function_may_throw(int x, float y, TRAPS) {
...
THROW_0("Exception message");
...
}
int main() {
this_function_may_throw(x_arg, y_arg, CHECK_0);
...
return 1;
}
На самом деле, составить полный и понятный отчет об ошибке довольно трудоемко. Взять, к примеру, уже упомянутые исходники HotSpot JVM — там целый модуль посвящен генерации hs_err.log в случае падения приложения. И есть свои тонкости. Например, надо учитывать, что код обработчика надо исполнять на отдельном стеке, т.к. ошибка могла быть вызвана переполнением стека; а еще, что SEGV могут возникать и внутри самого обработчка и т.д.
Сможем с помощью sigaction(). Тогда в обработчик будет передана подробная информация о SIGSEGV, включая адрес инструкции, на которой свалилась программа, адрес памяти, по которому обратились, а также контекст со значениями регистров процессора в момент падения.
Использовать sigaction() вместо signal(). Тогда в обработчик сигнала будет передан указатель на контекст, содержащий помимо прочего значения регистров в момент исключения. Изменив в контексте значение регистра PC, по возвращении из обработчика можно попасть в любую точку программы.
Можно не только теоретически, но и на практике это широко применяется в системном программировании. Например, в HotSpot JVM обработка SIGSEGV используется для ликвидации проверок на null, для ускоренной проверки на StackOverflow, для Safepoint-поллинга, для эмуляции memory barrier и для разных спекулятивных ловушек, связанных с обработкой особых ситуаций виртуальной машины без генерации дополнительного кода для обычных быстрых путей. Думаю, напишу об этих приемах отдельную статью.
При вызове статического метода есть проверка (так называемый class initialization barrier): если receiver класс не инциализирован, то вызывается его инициализация. §2.17.5 JVM specification говорит о том, что при инициализации класса в первую очередь происходит синхронизация на объекте java.lang.Class, представляющем этот класс.
Однако фокус состоит в том, что в процессе исполнения Java-приложения после инициализации класса JVM может заново перекомпилировать метод, вызывающий getInstance(), избавившись от ненужного class initialization barrier и оптимизировав таким образом вызов статического метода.
Чтение volatile поля в Java на x86 архитектуре ничем не отличается от чтения обычного поля. Отличается только запись volatile поля, которая сопровождается инструкцией lock add [esp], 0, служащей эффективным memory-barrier'ом. И то, это совсем далеко от того, что делается при синхронизации с помощью synchronized. В общем, не слушайте тех, кто говорит, что volatile равносилен synchronized. В HotSpot VM накладные расходы на доступ к volatile полям очень маленькие, и то только на запись.
Это распространенное заблуждение. Объект создастся не тогда, когда загрузится класс, а когда класс будет инициализирован. А инициализация класса произойдет в момент первого доступа к INSTANCE. А если у класса-синглтона вызывают прочие статические методы, которым не нужен этот самый instance, на мой взгляд, это говорит о недоработке в проектировании.
Вообще-то речь идет о Java. Там newLen += allowedChars[ch] выльется как минимум в два сравнения (range check). Да даже для C эта оптимизация не имеет смысла: newLen += (ch >= ' ') будет быстрее. Условных переходов в сгенерированном коде нет.
Byte[] намного меньше, т.к. новые объекты типа Byte не создаются — их всего 256, и они все закэшированы.
Объекты типа Short, Integer, Long занимают одинаково по 16 байт (на 32-битной платформе), т.к. заголовок объекта в HotSpot состоит из 8 байт, и сами объекты аллоцируются с выравниванием в 8 байт.
Сравните, например, ARM:
и x86:
Однако фокус состоит в том, что в процессе исполнения Java-приложения после инициализации класса JVM может заново перекомпилировать метод, вызывающий getInstance(), избавившись от ненужного class initialization barrier и оптимизировав таким образом вызов статического метода.
И чем же, по-вашему, вариант с enum не «ленивый»?
При >> мы получим 0 или -1, а при >>> 0 или 1.
Объекты типа Short, Integer, Long занимают одинаково по 16 байт (на 32-битной платформе), т.к. заголовок объекта в HotSpot состоит из 8 байт, и сами объекты аллоцируются с выравниванием в 8 байт.