Я не знаю, как конкретно это делает Java, но в целом вы вставляете опкоды «ловушки» в поток инструкций интерпретатора. В спецификации JVM есть два кода операций , которые кажутся специально разработанными для этой цели.
Если вы хотите знать наверняка, нет лучшего ответа, чем источник: http: // download .java.net / jdk6 / source /
Я могу объяснить, как мы это делаем, в CACAO VM (исследование только для JIT JVM). Сначала машинный код метода генерируется в некотором блоке памяти, выделенном в куче. После компиляции становится известна окончательная длина кода, и часть исполняемой памяти выделяется с использованием mmap
и флага PROT_EXEC
(соответствующий код CACAO здесь ). Затем машинный код копируется в область mmapped. После этого для многих архитектур требуется какой-то машинно-зависимый механизм очистки кеша. В качестве примера взгляните на функцию очистки кеша для PowerPC 64. В частности, на i386 и x86_64, нечего делать. После этого шага процессор готов к выполнению вновь созданного кода. В качестве альтернативы, уже выделенные страницы памяти можно пометить как исполняемые с помощью mprotect
. Обратите внимание, что mmap
/ mprotect
являются средствами Unix.
В Common Language Runtime есть таблица методов для каждого типа с записями, указывающими на машинный код или на собственную заглушку для управляемого JIT кода, а затем исправление таблицы методов с помощью указателя на только что созданный машинный код.
MSDN имеет более подробное объяснение в разделе MethodDesc
Эта запись в блоге Дейва Нотарио объясняет, как работает компилятор CLR JIT.