Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Копаемся в памяти JVM. Манипуляции с флагами

Anna | 4.06.2014 | нет комментариев
HotSpot JVM имеет уйма опций для отслеживания протекающего в виртуальной машине: PrintGC,PrintCompilationTraceClassLoading и т.п. Как правило, они включаются параметрами командной строки, скажем, -XX: PrintGCDetails. Впрочем порой появляется надобность включить либо отключить такой флаг непринужденно во время работы приложения, когда перезапуск JVM с другими параметрами немыслим. Этого дозволено добиться как штатным, так и хакерским методом, причем конечный и мощнее, и увлекательнее. Однако, внимания заслуживают оба.

Из данной статьи вы узнаете:
  • где обнаружить все флаги JVM, и на какие типы они делятся;
  • как прочитать либо установить флаг программно, применяя JMX;
  • как обнаружить надобную область в памяти виртуальной машины и испортить модифицировать ее.

Какими бывают флаги в HotSpot JVM

Список всех флагов с пояснениями доступен в исходниках OpenJDK: основная часть в globals.hpp наравне с дополнительными опциями архитектурыкомпилятора и G1 коллектора.

Как видно, флаги определяются различными макросами:

  • product и product_rw флаги дозволено задавать в командной строке ключиком -XX;
  • develop и notproduct неинтересны, от того что в официальных релизах JDK являются константами;
  • manageable флаги дозволено изменять в run-time через JMX;
  • experimental официально не поддерживаются (в частности, по причине неудовлетворительной протестированности), но могут быть включены на свой ужас и риск. Для модификации этих флагов требуется добавить ключ командной строки UnlockExperimentalVMOptions, скажем,
    -XX: UnlockExperimentalVMOptions -XX: TrustFinalNonStaticFields
  • diagnostic не предуготовлены для применения, помимо как в целях расследования задач виртуальной машины. Включить их дозволено только коллективно с UnlockDiagnosticVMOptions, скажем,
    -XX: UnlockDiagnosticVMOptions -XX: PrintAssembly

Дабы вывести все флаги, доступные в вашей версии JVM, совместно с их востребованными значениями, следует запустить JVM с параметром PrintFlagsFinal:

java -XX: PrintFlagsFinal

Работа с флагами через JMX

HotSpot разрешает программно прочитать либо установить значения некоторых флагов посредствомManagement API. Больше того, при включенном Remote Management это дозволено делать даже на удаленном сервере.

Раньше каждого, нужно получить экземпляр 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");

Прямой доступ к памяти JVM

К сожалению, комплект опций, изменяемых посредством JMX, невелик. Но чай флаги JVM — это лишь обыкновенные переменные, помещенные в адресном пространстве процесса. Если знать адрес переменной, по нему дозволено записать новое значение через Unsafe API. Остается обнаружить адрес JVM-флага. Задача непростая, от того что от запуска к запуску адрес будет меняться по свободе операционной системы. К счастью, Linux — крайне сговорчивая ОС, и с охотой выложит нам все нужные данные, если верно попросить.

  1. Вначале понадобится узнать, где лежит библиотека виртуальной машины libjvm.so, и по какому адресу она загружена. В этом поможет виртуальная файловая система proc, в частности, файл/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();
        }
    }
    
  2. Настал ключевой момент: откроем файл libjvm.so на чтение. При помощи нашего open-source ELF-парсера обнаружим в библиотеке символьную секцию, где среди символов присутствуют и все JVM флаги. Вновь же, никаких хаков, только чистая Java.
    ElfReader elfReader = new ElfReader(jvmLibrary);
    ElfSymbolTable symtab = (ElfSymbolTable) elfReader.section(".symtab");
    
  3. Прибавим к адресу символа базовый адрес библиотеки, полученный в п.1, и получим желанное место в памяти, где хранится значения флага. Сейчас его запросто поменяем через 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.

Если хотите узнать огромнее — приходите на конференцию по Java-спецтехнологиям Joker, которая состоится 15 октября в Санкт-Петербурге. От Одноклассников будет представлено три доклада, в том числе и по JVM.

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB