- PVSM.RU - https://www.pvsm.ru -
HotSpot JVM имеет множество опций для отслеживания происходящего в виртуальной машине: PrintGC
, PrintCompilation
, TraceClassLoading
и т.п. Как правило, они включаются параметрами командной строки, например, -XX:+PrintGCDetails
. Однако порой возникает необходимость включить или выключить такой флаг непосредственно во время работы приложения, когда перезапуск JVM с другими параметрами невозможен. Этого можно добиться как штатным, так и хакерским способом, причем последний и мощнее, и интереснее. Впрочем, внимания заслуживают оба.
Список всех флагов с пояснениями доступен в исходниках OpenJDK: основная часть в globals.hpp [2] наряду с дополнительными опциями архитектуры [3], компилятора [4] и G1 коллектора [5].
Как видно, флаги определяются разными макросами:
UnlockExperimentalVMOptions
, например,
-XX:+UnlockExperimentalVMOptions -XX:+TrustFinalNonStaticFields
UnlockDiagnosticVMOptions
, например,
-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly
Чтобы вывести все флаги, доступные в вашей версии JVM, вместе с их актуальными значениями, следует запустить JVM с параметром PrintFlagsFinal
:
java -XX:+PrintFlagsFinal
HotSpot позволяет программно прочитать или установить значения некоторых флагов посредством Management API [6]. Более того, при включенном Remote Management [7] это можно делать даже на удаленном сервере.
Прежде всего, необходимо получить экземпляр MXBean с именем com.sun.management:type=HotSpotDiagnostic
.
import com.sun.management.HotSpotDiagnosticMXBean;
import java.lang.management.ManagementFactory;
import javax.management.MBeanServer;
...
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
HotSpotDiagnosticMXBean bean = ManagementFactory.newPlatformMXBeanProxy(
server,
"com.sun.management:type=HotSpotDiagnostic",
HotSpotDiagnosticMXBean.class);
Метод bean.getVMOption(String option)
позволит узнать текущее значение JVM-опции,
а bean.setVMOption(String option, String newValue)
— задать новое.
Если прочитать можно любой флаг, то изменению поддаются только manageable
.
Метод bean.getDiagnosticOptions()
вернет список всех manageable
опций.
Пример:
// Включение флага JVM, отвечающего за вывод ReentrantLock и т.п. в thread dump
bean.setVMOption("PrintConcurrentLocks", "true");
К сожалению, набор опций, изменяемых посредством JMX, невелик. Но ведь флаги JVM — это лишь обычные переменные, размещенные в адресном пространстве процесса. Если знать адрес переменной, по нему можно записать новое значение через Unsafe API [8]. Остается найти адрес JVM-флага. Задача непростая, поскольку от запуска к запуску адрес будет меняться по воле операционной системы. К счастью, Linux — весьма сговорчивая ОС, и охотно выложит нам все необходимые сведения, если правильно попросить.
libjvm.so
, и по какому адресу она загружена. В этом поможет виртуальная файловая система proc [9], в частности, файл /proc/self/maps
, где перечислены все регионы виртуального адресного пространства текущего процесса. Найдем в нем строчку, оканчивающуюся на /libjvm.so
.
2b6707956000-2b67084b8000 r-xp 00000000 68:02 1823284 /usr/java/jdk1.7.0_40/jre/lib/amd64/server/libjvm.so
Первое число (0x2b6707956000) и будет базовым адресом, по которому загружена библиотека.
Заметьте, все это можно проделать на чистой Java!
private String findJvmMaps() throws IOException {
BufferedReader reader = new BufferedReader(new FileReader("/proc/self/maps"));
try {
for (String s; (s = reader.readLine()) != null; ) {
if (s.endsWith("/libjvm.so")) {
return s;
}
}
throw new IOException("libjvm.so not found");
} finally {
reader.close();
}
}
libjvm.so
на чтение. При помощи нашего open-source ELF-парсера [10] найдем в библиотеке символьную секцию, где среди символов присутствуют и все JVM флаги. Опять же, никаких хаков, только чистая Java.
ElfReader elfReader = new ElfReader(jvmLibrary);
ElfSymbolTable symtab = (ElfSymbolTable) elfReader.section(".symtab");
Unsafe.putInt
:
private ElfSymbol findSymbol(String name) {
for (ElfSymbol symbol : symtab) {
if (name.equals(symbol.name()) && symbol.type() == ElfSymbol.STT_OBJECT) {
return symbol;
}
}
throw new NoSuchElementException("Symbol not found: " + name);
}
public void setIntFlag(String name, int value) {
ElfSymbol symbol = findSymbol(name);
unsafe.putInt(baseAddress + symbol.value(), value);
}
public void setBooleanFlag(String name, boolean value) {
setIntFlag(name, value ? 1 : 0);
}
Как видите, в Java без единой строчки нативного кода можно управлять runtime окружением, в том числе и самой виртуальной машиной. Однако помните, что использование недокументированных методов сопряжено с риском, и мы ни в коем случае не рекомендуем их к применению в production.
Полный исходный код эксперимента вы найдете на GitHub [11].
Если хотите узнать больше — приходите на конференцию по Java-технологиям Joker [12], которая состоится 15 октября в Санкт-Петербурге. От Одноклассников будет представлено три доклада, в том числе и по JVM.
Автор: apangin
Источник [13]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/java/44418
Ссылки в тексте:
[1] JMX: http://docs.oracle.com/javase/tutorial/jmx/index.html
[2] globals.hpp: http://hg.openjdk.java.net/hsx/hotspot-main/hotspot/file/tip/src/share/vm/runtime/globals.hpp
[3] архитектуры: http://hg.openjdk.java.net/hsx/hotspot-main/hotspot/file/tip/src/cpu/x86/vm/globals_x86.hpp
[4] компилятора: http://hg.openjdk.java.net/hsx/hotspot-main/hotspot/file/tip/src/share/vm/opto/c2_globals.hpp
[5] G1 коллектора: http://hg.openjdk.java.net/hsx/hotspot-main/hotspot/file/tip/src/share/vm/gc_implementation/g1/g1_globals.hpp
[6] Management API: http://docs.oracle.com/javase/7/docs/api/java/lang/management/package-summary.html
[7] Remote Management: http://docs.oracle.com/javase/tutorial/jmx/remote/jconsole.html
[8] Unsafe API: http://mishadoff.github.io/blog/java-magic-part-4-sun-dot-misc-dot-unsafe/
[9] proc: http://man7.org/linux/man-pages/man5/proc.5.html
[10] open-source ELF-парсера: https://github.com/odnoklassniki/one-elf
[11] на GitHub: https://github.com/odnoklassniki/one-elf/tree/master/test/one/jvm
[12] Joker: http://jokerconf.com
[13] Источник: http://habrahabr.ru/post/195004/
Нажмите здесь для печати.