Complete interpretation of jstat tool principle of JVM source code analysis

Summary

Jstat is a tool that comes with hotspot. Like Java, it is also located under JAVA_HOME/bin. Through this tool, we can know the gc, compiler, class, memory and other related situations of the current process in real time. Specifically, we can see which types of data we support through jstat options, such as JDK8

-class-compiler
-gc
-gccapacity
-gccause
-gcmetacapacity
-gcnew
-gcnewcapacity
-gcold
-gcoldcapacity
-gcutil
-printcompilation

Output of jstat

Jstat is widely used. The most common usage is jstat gcutil. The output is as follows:

~ ᐅ jstat -gcutil 692 1000
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT  0.00  41.49  59.79  83.66  89.92  78.74    295    5.436    10    3.855    9.291
  0.00  41.49  59.80  83.66  89.92  78.74    295    5.436    10    3.855    9.291
  0.00  41.49  59.80  83.66  89.92  78.74    295    5.436    10    3.855    9.291
  0.00  41.49  59.80  83.66  89.92  78.74    295    5.436    10    3.855    9.291
  0.00  41.49  59.80  83.66  89.92  78.74    295    5.436    10    3.855    9.291

How to define and calculate each column? In fact, there is a file called jstat? Options in tools.jar, which defines the above output results of each type, such as gcutil

option gcutil {
  column {
    header "^S0^"    /* Survivor 0 Space - Percent Used */
    data (1-((sun.gc.generation.0.space.1.capacity - sun.gc.generation.0.space.1.used)/sun.gc.generation.0.space.1.capacity)) * 100
    scale raw
    align right
    width 6
    format "0.00"
  }
  column {
    header "^S1^"    /* Survivor 1 Space - Percent Used */
    data (1-((sun.gc.generation.0.space.2.capacity - sun.gc.generation.0.space.2.used)/sun.gc.generation.0.space.2.capacity)) * 100
    scale raw
    align right
    width 6
    format "0.00"
  }
  column {
    header "^E^"    /* Eden Space - Percent Used */
    data (1-((sun.gc.generation.0.space.0.capacity - sun.gc.generation.0.space.0.used)/sun.gc.generation.0.space.0.capacity)) * 100
    align right
    scale raw
    width 6
    format "0.00"
  }
  column {
    header "^O^"    /* Old Space - Percent Used */
    data (1-((sun.gc.generation.1.space.0.capacity - sun.gc.generation.1.space.0.used)/sun.gc.generation.1.space.0.capacity)) * 100
    align right
    scale raw
    width 6
    format "0.00"
  }
  column {
    header "^M^"    /* Metaspace Space - Percent Used */
    data (1-((sun.gc.metaspace.capacity - sun.gc.metaspace.used)/sun.gc.metaspace.capacity)) * 100
    align right
    width 6
    scale raw
    format "0.00"
  }
  column {
    header "^CCS^"    /* Compressed Class Space Space - Percent Used */
    data (1-((sun.gc.compressedclassspace.capacity - sun.gc.compressedclassspace.used)/sun.gc.compressedclassspace.capacity)) * 100
    align right
    width 6
    scale raw
    format "0.00"
  }
  column {
    header "^YGC^"    /* Young Generation Collections */
    data sun.gc.collector.0.invocations
    align right
    width 6
    format "0"
  }
  column {
    header "^YGCT^"    /* Young Generation Collection Time */
    data sun.gc.collector.0.time/sun.os.hrt.frequency
    align right
    scale sec
    width 8
    format "0.000"
  }
  column {
    header "^FGC^"    /* Full Collections */
    data sun.gc.collector.1.invocations
    align right
    width 5
    scale raw
    format "0"
  }
  column {
    header "^FGCT^"    /* Full Collection Time */
    data sun.gc.collector.1.time/sun.os.hrt.frequency
    align right
    scale sec
    width 8
    format "0.000"
  }
  column {
    header "^GCT^"    /* Total Garbage Collection Time */
    data (sun.gc.collector.0.time + sun.gc.collector.1.time)/sun.os.hrt.frequency
    align right
    width 8
    scale sec
    format "0.000"
  }
}

From the above definition, we know what each column of gcutil means and how to calculate it. Some variables like sun.gc.generation.0.space.0.capacity are created in the jvm and updated in real time

How jstat gets the values of these variables

The variable value is obviously obtained from the target process, but how? local socket or memory share? In fact, it comes from a shared file called PerfData, which mainly refers to the file / TMP / hsperfdata

PerfData file

File creation

The existence of this file depends on two parameters: UsePerfData and PerfDisableSharedMem. If - XX:+PerfDisableSharedMem or - XX:-UsePerfData is set, this file will not exist. By default, PerfDisableSharedMem is closed and UsePerfData is open, so the PerfData file exists by default. For the two parameters of UsePerfData and PerfDisableSharedMem, here are the highlights:

  • UsePerfData: if the UsePerfData parameter is turned off, perf memory will not be created during jvm startup, and will not be saved during jvm operation. It is turned on by default

  • PerfDisableSharedMem: this parameter determines whether the memory for storing PerfData can be shared. That is to say, whether this parameter is set or not, the jvm will allocate a piece of memory to store PerfData when it is started, just to say whether this PerfData is a visible problem for other processes. If this parameter is set, it means that it cannot be shared. At this time, other processes will not access it With this memory, for example, our jps, jstat, etc. can not work. By default, this parameter is off, that is, sharing is supported by default

The specific code is in perfmemory:: create? Memory? Region

  if (PerfDisableSharedMem) {    // do not share the memory for the performance data.
    _start = create_standard_memory(size);
  }  else {
    _start = create_shared_memory(size);    if (_start == NULL) {      // creation of the shared memory region failed, attempt
      // to create a contiguous, non-shared memory region instead.
      //
      if (PrintMiscellaneous && Verbose) {
        warning("Reverting to non-shared PerfMemory region.\n");
      }
      PerfDisableSharedMem = true;
      _start = create_standard_memory(size);
    }
  }

File deletion

When will this file be deleted? Normally, when the process exits, it will be deleted automatically. However, in some extreme cases, such as kill-9, the signal jvm can't capture, so the process exits directly without finishing work. At this time, you will find that although the process is gone, the file still exists. If the file is kept all the time, it can only be deleted manually. Considering this situation, the jvm will make a judgment when any next java process (for example, we execute jps) of the current user gets up, traverse the process files under / TMP / hsperfdata < user > one by one, and see whether the process still exists. If not, delete the file directly The specific operation to determine whether there is an exception is to send a kill-0 signal.

File update

Because this file is mapped to memory through mmap and jstat is read from PerfData directly through DirectByteBuffer, as long as the value in memory changes, the value we see from jstat will change. When the value in memory changes depends on the parameter - XX:PerfDataSamplingInterval, which is 50ms by default, that is, 50ms update The primary value is basically real-time.

PerfData other related VM parameters

  • -20: Perfdatamemorysize: specify / TMP / hsperfdata The default size of the perfData file is 32KB. If the user sets this value, the jvm will automatically align with the page size of the os. For example, the default size of pagesize in linux is 4KB. If you set 31KB, 32KB will be automatically allocated

  • -20: + perfdatasavetofile: whether to save the data in PerfData to a specific file when the process exits. The file path is specified by the following parameters, otherwise it will be in the current directory

  • -20: Perfdatasavefile: Specifies the path to save the PerfData file

Pit in jstat

I think of two pits for the time being:

  • After a normal Background CMS GC, it was found that the value of FGC was added twice. Later, it was found that the main reason is that CMS has two stages, init mark and remark, which will suspend the application. At the same time, because it is gc for old, it was calculated twice

  • The usage of metaspace in JDK8 is not accurate. For example, the value of CCSC indicates Compressed Class Space Capacity, but the calculation of this value is not the value of reserve, so we may find that the usage of metaspace is very small, but the usage rate is very high through jstat, so it's better to use jmx to get those values for a calculation

size_t CompressedClassSpaceCounters::capacity() {  return MetaspaceAux::committed_bytes(Metaspace::ClassType);
}

Welcome to PerfMa community, recommended reading

Frequent CMS GC caused by a JVM parameter

Kafka in big data, what's the best?

Tags: Programming jvm Java socket Linux

Posted on Tue, 10 Mar 2020 02:48:46 -0700 by tinkertron