📜 ⬆️ ⬇️

Digging in JVM memory. Manipulations with flags



HotSpot JVM has many options for tracking what is happening in the virtual machine: PrintGC , PrintCompilation , TraceClassLoading , etc. Typically, they are included with command line options, for example, -XX:+PrintGCDetails . However, sometimes it becomes necessary to enable or disable such a flag directly while the application is running, when it is not possible to restart the JVM with other parameters. This can be achieved both in a regular and hacking way, the latter being both more powerful and more interesting. However, both deserve attention.

From this article you will learn:



')

What are the flags in HotSpot JVM


A list of all flags with explanations is available in the OpenJDK sources: the main part in globals.hpp along with additional options for the architecture , compiler and G1 collector .

As you can see, the flags are defined by different macros:


To display all the flags available in your version of the JVM, along with their actual values, you must run the JVM with the PrintFlagsFinal parameter:
 java -XX:+PrintFlagsFinal 


Work with flags via JMX


HotSpot allows you to programmatically read or set the values ​​of certain flags via the Management API . Moreover, with Remote Management enabled, this can be done even on a remote server.

First of all, you need to get an instance of MXBean named 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); 


The method bean.getVMOption(String option) will let you know the current value of the JVM option,
and bean.setVMOption(String option, String newValue) - set a new one.
If you can read any flag, then changes are only manageable .
The bean.getDiagnosticOptions() method will return a list of all manageable options.

Example:
 //   JVM,    ReentrantLock  ..  thread dump bean.setVMOption("PrintConcurrentLocks", "true"); 


JVM Direct Memory Access


Unfortunately, the set of options that are changed by JMX is small. But the JVM flags are just ordinary variables located in the address space of the process. If you know the address of a variable, you can write a new value by it through the Unsafe API . It remains to find the address of the JVM-flag. The task is not easy, since from launch to launch the address will change at the behest of the operating system. Fortunately, Linux is a very compliant OS, and will willingly give us all the necessary information, if asked correctly.

  1. First you need to find out where the virtual machine library libjvm.so , and what address it is loaded at. This will help the proc virtual file system, in particular, the file /proc/self/maps , which lists all regions of the virtual address space of the current process. Find in it a line ending in /libjvm.so .
     2b6707956000-2b67084b8000 r-xp 00000000 68:02 1823284 /usr/java/jdk1.7.0_40/jre/lib/amd64/server/libjvm.so 

    The first number (0x2b6707956000) will be the base address where the library is loaded.

    Note that all this can be done in pure 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. The key point is: open the file libjvm.so for reading. With the help of our open-source ELF parser, we find a symbol section in the library, where all the JVM flags are present among the symbols. Again, no hacks, just pure java.
     ElfReader elfReader = new ElfReader(jvmLibrary); ElfSymbolTable symtab = (ElfSymbolTable) elfReader.section(".symtab"); 

  3. Add to the address of the symbol the base address of the library, obtained in claim 1, and we obtain the desired place in the memory where the flag values ​​are stored. Now we can easily change it via 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); } 



Conclusion


As you can see, in Java, without a single line of native code, you can control the runtime environment, including the virtual machine itself. However, remember that the use of undocumented methods is risky, and we in no way recommend them for use in production.

You can find the full source code for the experiment on GitHub .

If you want to learn more, come to the Joker Java Technology Conference, which will be held on October 15 in St. Petersburg. From Odnoklassniki three reports will be presented, including on the JVM.

Source: https://habr.com/ru/post/195004/


All Articles