Process of MinorGC (copy algorithm)

Copy -> empty -> swap

  1. Copy Eden, SurvivorFrom to SurvivorTo, age +1
    • First, when Eden is full, the first GC will be performed, and the surviving objects will be copied to SurvivorFrom. When Eden and From will be scanned for GC again, and garbage collection will be performed for these two areas.
    • Objects that survive this collection are copied directly To the To section with an age of +1. If an object has reached the old age standard, copy to the old age.
  2. Clear Eden and SurvivorFrom
    • Clear objects in Eden and SurvivorFrom
  3. SurvivorTo and SurvivorFrom are exchanged
    • The SurvivorTo and SurvivorFrom are swapped, and the original SurvivorTo is called the SurvivorFrom zone for the next GC.
    • Some objects are copied back and forth between the From and To sections, swapping 15 times (JVM parameters)MaxTenuringThresholdThe default parameter is 15), and if it still survives, it will be saved to the old age.

GC Roots

Garbage: Space in memory that is no longer being used is garbage.

Reachability analysis algorithm: The basic idea is to search down from a series of objects named GC Roots as a starting point. If an object is not connected to GC Roots by any reference chain, it indicates that the object is unavailable.

GC Roots is a set of must-active references.

Possible objects for GC Roots are:

  1. Objects referenced in the virtual machine stack (the local variable area in the stack frame, also known as the local variable table).
  2. The object referenced by the class static property in the method area.
  3. The object referenced by the constant in the method area.
  4. Objects referenced by JNI (Native methods) in the Native method stack.

The JVM parameter

JVM parameter types

1. Standard configuration parameters: Remain stable between JDK versions

- java -version
- java -help
- java -showversion
Copy the code

2. The parameter X

-xint: explains execution. -xcomp: compiles native code the first time it is used. -xmixed: defaults to compile before executionCopy the code

3. XX parameters

  1. Boolean type

    • XX: + / - properties
    • + indicates enabled, and – indicates disabled
    • The sampleTake whether to print GC collection details as an example:
      • First, use JPS -L to check the current running process ID

      • Then use jinfo-flag PrintGCDetails to check the process ID

      • If enabled, the result should be -xx :+PrintGCDetails, otherwise -xx: -printgcDetails

  2. KV type

    • -XX:key=value
    • Take MetaspaceSize as an example: use the same method as aboveJinfo-flag MetaspaceSize INDICATES the process IDTo look at it.
    • -XmsIs equivalent to-XX:InitialHeapSize.-XmxIs equivalent to-XX:MaxHeapSize

How to view JVM parameter defaults

  1. usejava -XX:+PrintFlagsInitalView the JVM initial parameters
  2. usejava -XX:+PrintFlagsFinal -versionView updated parameters. Parameter used in=Note Is an unmodified parameter: =Note The parameter is manual or modified by the JVM.
  3. usejava -XX:+PrintCommandLineFlags

Common JVM parameters

After JDK 1.8, persistent generation was eliminated and replaced by meta-space. The new generation consists of Eden + SurvivorFrom + SurvivorTo and the size is set with -xMN. The JVM heap consists of both the new generation and the old generation, and is sized by -xms and -xmx. The meta-space is no longer part of the heap.

The difference between a meta-space and a permanent generation is that the permanent generation uses the HEAP memory of the JVM, whereas the meta-space is not in the virtual machine but uses native physical memory.

By default, the size of the meta-space is limited only by the local memory, the metadata of the class is put into native memory, and the string pool and static variables of the class are put into the Java heap, so that the amount of metadata of the class can be loaded is not controlled by MaxPermSize, but by the actual available space of the system.

  1. -Xms
    • Is equivalent to-XX:InitialHeapSize
    • The default size is 1/64 of the physical memory
  2. -Xmx
    • Is equivalent to-XX:MaxHeapSize
    • Maximum memory allocated. The default value is 1/4 physical memory
  3. -Xss
    • Is equivalent to-XX:ThreadStackSize
    • Set the size of a single thread stack
    • The default value for Linux is 1024KB
    • The default value of OS X is 1024KB
    • The Windows default depends on virtual memory
  4. -Xmn
    • Set new generation size: default is 1/3 of heap space
  5. -XX:MetaspaceSize
    • Metaclases are similar to permanent generations in that they are implementations of method regions. The meta-space is not in the virtual machine, but uses local memory. By default, the size of a meta-space is limited only by local memory.
    • But that doesn’t mean there won’t be an OOM in the metacolor, because the default memory size is limited
  6. -XX:+PrintGCDetails
    • The detailed GC mobile phone logs are displayed
[GC [PSYoungGen: 2048K -> 496K(2560K)] 2048K->777K(9728k)]
Copy the code
  • [GC [PSYoungGen:Represents GC type
  • 2048KYoungGC memory usage before the YoungGC generation
  • 496KIndicates the memory usage of the younger generation after YoungGC
  • (2560K)Represents the total size of the Cenozoic era
  • 2048KRepresents the JVM heap memory footprint before YoungGC
  • 777KRepresents JVM heap memory footprint after YoungGC
  • 9728KRepresents the total JVM heap size
[Full GC (System) [PSYoungGen:3408K->0K(296688k)] [PSOldGen:0K->3363K(682688K)]3408K->3363K(981376K)[Metaspace:10638K->10638K(131072K)]]
Copy the code

In Full GC, you can see the pre-gc, post-GC, and total sizes of new generation, old generation, total heap memory, and meta-space.

  1. -XX:SurvivorRatio
    • Set the ratio of Eden and S0/S1 in the new generation
    • The default value is 8, indicating that the ratio is 8:1:1
  2. -XX:NewRatio
    • Sets the ratio of new generation to old age in the heap
    • The value set is the ratio of the old generation to the new generation
  3. -XX:MaxTenuringThreshold
    • Set the maximum garbage age. Default is 15
    • In Java8, the threshold ranges from 0 to 15
    • Set this parameter to a small value, which is suitable for many applications in the old era.
    • If the value is set to a larger value, the object can survive longer in the Cenozoic era and the probability of recycling in the Cenozoic era can be increased.

Four types of references

SoftReference, WeakReference, and PhantomReference all inherit from Reference class, which is a subclass of Object class.

Strong reference

When memory runs out, the JVM starts garbage collection, but strongly referenced objects are not collected even if OOM is present. Assigning an object to a reference variable is a strong reference. As long as there is a strong reference to an object, the object is reachable and will not be reclaimed by the JVM.

Soft references

Objects that have only soft references will not be reclaimed when the system is full and will be reclaimed when the system is out of memory. Soft references are usually used in memory-sensitive programs, such as caches.

Application scenario (Cache) : If an application reads a large number of local images, I/O performance deteriorates each time the images are read. Memory overflow may occur if the images are loaded to the memory at a time.

You can use HashMap to store the mapping between the path of the image and the soft reference associated with the image object. When memory runs out, the JVM automatically reclaims the space occupied by these cached image objects, effectively avoiding OOM problems.

Map<String, SoftReference<Bitmap>> imageCache = new HashMap<>();
Copy the code

A weak reference

As soon as GC runs, the object’s memory is reclaimed, regardless of whether the JVM has enough memory.

WeakHashMap

Compared with HashMap, when key is reclaimed, the corresponding key-value pair in the Map will no longer exist for the elements in WeakHashMap.

public class WeakHashMapDemo {
    public static void main(String[] args) {
        myHashMap();
        System.out.println("= = = = = = = = = = = = = = = = = = = = = =");
        myWeakHashMap();
    }

    private static void myWeakHashMap(a) {
        WeakHashMap<Integer,String> map=new WeakHashMap<>();

        Integer k=new Integer(1);
        String v="str";

        map.put(k,v);
        System.out.println(map);

        k=null;
        System.out.println(map);

        System.gc();
        System.out.println(map);
    }

    private static void myHashMap(a) {
        HashMap<Integer,String> map=new HashMap<>();

        Integer k=new Integer(1);
        String v="str";

        map.put(k,v);
        System.out.println(map);

        k=null; System.out.println(map); System.gc(); System.out.println(map); }}Copy the code

The results

{1=str}
{1=str}
{1=str}
======================
{1=str}
{1=str}
{}
Copy the code

Reference queue

public class ReferenceQueueDemo {
    public static void main(String[] args) throws InterruptedException {
        Object o1=new Object();
        ReferenceQueue<Object> referenceQueue=new ReferenceQueue<>();
        WeakReference<Object> weakReference=new WeakReference<>(o1,referenceQueue);
        System.out.println(o1);
        System.out.println(weakReference.get());
        System.out.println(referenceQueue.poll());

        System.out.println();
        o1=null;
        System.gc();
        Thread.sleep(1000); System.out.println(o1); System.out.println(weakReference.get()); System.out.println(referenceQueue.poll()); }}Copy the code
java.lang.Object@1b6d3586
java.lang.Object@1b6d3586
null

null
null
java.lang.ref.WeakReference@4554617c
Copy the code

Weak, soft, and virtual references are placed in the reference queue before GC.

Phantom reference

A virtual reference is, as its name implies, a virtual reference, which does not determine the declaration cycle of an object. If an object holds only virtual references, it can be reclaimed at any time, just as if there were no references. Virtual references cannot be used alone or through which objects can be accessed; they must be used in conjunction with reference queues.

The main function of virtual reference is to track the state of objects being garbage collected. It only provides a mechanism to ensure that objects do something after being finalize. This means that an object has entered the finalization stage and can be recycled to achieve a more flexible collection operation than finalization mechanisms.

The only purpose of setting a virtual reference is to receive a system notification or add further processing when the object is reclaimed.

public class PhantomReferenceDemo {
    public static void main(String[] args) throws InterruptedException {
        Object o1=new Object();
        ReferenceQueue<Object> referenceQueue=new ReferenceQueue<>();
        PhantomReference<Object> phantomReference=new PhantomReference<>(o1,referenceQueue);

        System.out.println(o1);
        System.out.println(phantomReference.get());
        System.out.println(referenceQueue.poll());

        System.out.println();
        o1=null;
        System.gc();
        Thread.sleep(1000); System.out.println(o1); System.out.println(phantomReference.get()); System.out.println(referenceQueue.poll()); }}Copy the code

Running results:

java.lang.Object@1b6d3586
null
null

null
null
java.lang.ref.PhantomReference@4554617c
Copy the code

As you can see, the get() return value of a virtual reference is always null.

conclusion

The associated queue can be specified when the reference is created, and the reference is added to the reference queue when the GC frees the object’s memory. If the program finds that a virtual reference has been added to the reference queue, it can take the necessary action before the memory of the referenced object is reclaimed, acting as a notification mechanism. In this way, the JVM allows us to do whatever we want after the object is destroyed.

OOM

java.lang.StackOverflowError

When the stack is called too deep in a recursive call, resulting in stack overflow errors. The inheritance relationship is as follows:

Throwable -> Error -> VirtualMachineError -> StackoverflowError
Copy the code

java.lang.OutOfMemoryError: Java heap space

If there are too many objects or large objects, the heap content exceeds the maximum size of the heap, resulting in a heap overflow.

java.lang.OutOfMemoryError: GC overhead limit exceeded

The GC reclaim time process throws such an exception. Too long is defined as more than 98% of the time spent doing GC and less than 2% of the heap memory reclaimed. In extreme cases, multiple GCS in a row only collect less than 2%.

What would happen if there were no GC overhead limit error: the memory cleared by the GC quickly fills up again, and the broken GC executes again, creating a vicious cycle where the CPU usage is 100% all the time, but the GC produces nothing.

public class GCOverheadDemo {
    public static void main(String[] args) {
        int i = 0;
        List<String> list = new ArrayList<>();
        
        while (true) { list.add(String.valueOf(++i).intern()); }}}Copy the code

java.lang.OutOfMemoryError: Direct buffer memory

Writing NIO programs often use ByteBuffer to read or write data. It can use the Native library to allocate out-of-heap memory directly, and then operate as a reference to that memory via a DirectByteBuffer object stored in the Java heap. This can significantly improve performance in some scenarios because it avoids copying data back and forth between the Java heap and Native heap.

Bytebuffer. allocate(capability) allocates JVM heap memory, which is under GC jurisdiction and is relatively slow due to copying.

ByteBuffer. AllocateDirect (capability) is assigned OS local memory, do not belong to the scope of the jurisdiction of the GC, because do not need memory copy so speed is relatively fast.

If local memory is constantly allocated and heap memory is rarely used, then the JVM does not need to perform GC and DirectByteBuffer objects are not collected, resulting in a heap full of memory, but local memory has been used up.

java.lang.OutOfMemoryError: Unable to create new native thread

This exception occurs when there are high concurrent requests to the server.

Causes:

  1. The application creates too many threads. An application process creates more threads than the system can handle
  2. The server does not allow applications to create so many threads, and the default number of threads created by a single process on Linux is 1024.

Solutions:

  1. Find ways to reduce the number of threads created
  2. For applications that really need to create a lot of threads, you can modify the Linux server configuration to extend its limits

java.lang.OutOfMemoryError: metaspace

MetaSpace is the implementation of the method area in HotSpot, which does not live in virtual machine memory but uses local memory, i.e. the metadata of the class is stored in MetaSpace native memory.

It contains: information about classes loaded by the virtual machine, constant pools, static variables, and just-in-time compiled code.

Garbage collector

Four ideas for garbage collectors

Serial

Serial garbage collectors, designed for single-threaded environments and using only one thread for garbage collection, suspend all user threads and are not suitable for server environments.

The procedure is: program run -> single GC thread -> program run

Parallel

Parallel garbage collector, multiple garbage collection threads work in parallel, at this time the user thread is suspended, suitable for scientific computing, big data processing and other weak interaction scenarios.

Procedure: Program run -> Multiple GC threads run in parallel -> program run

CMS

Concurrent garbage collector, where the user thread and GC thread execute simultaneously without the need to suspend the user thread, is suitable for scenarios where response time is critical (strong interaction).

Procedure: Initial mark (pause, single thread) -> concurrent mark (GC thread and user thread execute simultaneously) -> Final mark (pause, multiple GC threads) -> Clear

G1

The G1 garbage collector divides heap memory into regions and collects them concurrently.

Look at the default garbage collector

Use Java -xx :+PrintCommandLineFlags -version to view the default garbage collector.

In java8, the default is -xx :+UseParallelGC.

What are the default garbage collectors

There are several types of Java GC:

  • UseSerialGC
  • UseParallelGC
  • UseConcMarkSweepGC
  • UseParNewGC
  • UseParallelOldGC
  • UseG1GC

Of the seven garbage collectors in the JVM, Serial Old has been deprecated, so there are six of them in the GC source code.

Seven garbage recyclers

In the new generation are:

  1. Serial
  2. Parallel Scavenge
  3. ParNew

In the old days:

  1. Serial Old
  2. Parallel Compacting
  3. CMS

The one that can do both is the G1.

DefNew: Default New Generation
Tenured: Old
ParNew: Parallel New Generation
PSYoungGen: Parallel Scavenge
ParOldGen: Parallel Old Generation
Copy the code

New generation Serial collector: Serial

A single-threaded collector, during GC, must suspend all other worker threads until the GC is complete.

Client

JVM parameters: – XX: + UseSerialGC after open, new generation using Serial use the Old s Serial Old, new and Old age are using Serial recycle collector, new generation using replication algorithm, and use the Old s tag – sorting algorithm.

In GC logs, the new generation uses DefNew and the old generation uses Tenured.

The new generation of parallel collector: ParNew

Using multiple threads for garbage collection, the STW suspends other threads until the GC ends.

The default garbage collector for the new generation

JVM parameters: -xx :+UseParNewGC. When enabled, the new generation uses ParNew, the Old generation uses Serial Old, the new generation uses the copy algorithm, and the Old generation uses the mark-collation algorithm. Note that ParNew + Serial Old is no longer recommended.

In GC logs, the new generation uses ParNew and the old generation uses Tenured.

The new generation of Parallel collector: Parallel Insane

The Parallel Collector, similar to the ParNew collector, is a new generation garbage collector that uses replication algorithms, is a Parallel multithreaded garbage collector, and is throughput first.

It focuses on throughput (run user code time /(run user code time + garbage collection time). High throughput means efficient use of CPU time and is suitable for tasks that perform operations in the background without much interaction.

An important difference between the Parallel Scavenge and ParNew collectors is the adaptive adjustment strategy. An adaptive adjustment policy enables a VM to collect performance monitoring information based on the current system running status and dynamically adjust these parameters to provide the most appropriate pause time or maximum throughput.

The JVM argument -xx :+UseParallelGC has the same effect as using -xx :+UseParallelOldGC. The two collectors are used together by default, so they activate each other. When enabled, the new generation uses the copy algorithm and the old generation uses the mark-collation algorithm.

When the collector is enabled, the new generation outputs PSYoungGen and the old generation outputs ParOldGen in the GC log.

In Java 8, this collector is the default collector.

Parallel collector: Parallel Old

The Parallel Old collector is an older version of the Parallel Insane, using the multithreaded mark-collation algorithm, available in JDK 1.6.

Prior to JDK 1.6, the New generation used the Parallel Scavenge avenge to take precedence over the Serial Insane.

In JDK 1.8 and later, the default is the Parallel Insane + Parallel Old insane, ensuring overall throughput first.

JVM parameters: -xx :+UseParallelOldGC, the new generation to use the Parallel Insane, the older generation to use ParallelOld.

When the collector is enabled, the new generation outputs PSYoungGen and the old generation outputs ParOldGen in the GC log.

Old age concurrent mark clearing collector: CMS

CMS (Councurrent Mark Sweep) is a collector whose goal is to obtain the shortest recovery pause time. It is suitable for application on the server of Internet or B/S system. In this kind of application, it is the corresponding speed of the server and the system pause time is expected to be the shortest. The CMS is suitable for server applications with large heap memory and CPU cores.

JVM parameters: -xx :+UseConcMarkSweepGC. If this parameter is enabled, -xx :+UseParNewGC is automatically enabled. A collector combination of ParNew + CMS + Serail Old is used, with Serial Old as a backup collector for CMS errors.

  1. Initial tag: Tag objects directly associated with GC Roots, fast and requires STW.
  2. Concurrent marking: The process of GC Roots tracing, working with the user thread without pausing the worker thread, marking all objects.
  3. Relabelling: Corrects records of objects that were marked during concurrent marking because the user program continued to run, requiring STW.
  4. Concurrent cleanup: Clears unreachable GC Roots and works with the user thread without suspending the worker thread.

Because the GC thread works concurrently with the user during the longest concurrent markup and concurrent cleanup, the CMS GC and the user thread generally execute concurrently.

Advantages: Concurrent collection, low pauses.

Disadvantages:

  • Concurrent execution puts heavy pressure on CPU resources: Concurrent execution causes the CMS to occupy heap memory during collection. Therefore, CMS must complete garbage collection before the Old heap runs out of memory, otherwise, if CMS fails to collect, the guarantee mechanism will be triggered, and Serial Old will GC in STW mode, resulting in a large pause time
  • The use of tag removal algorithms results in a large number of fragments

Serial Old collector

Serial Old is an older version of the Serial collector. It is a single-threaded collector that uses the mark-collation algorithm and is the default Old collector of the Client.

In Server mode:

  • Match the Insane prior to JDK 1.5.
  • As an alternative garbage collection solution to the CMS collector in the old days.

How do I choose a garbage collector

  • Single CPU or small memory, single machine program:-XX:+UseSerailGC
  • Multi-cpu, requiring maximum throughput, such as background computing applications:-XX:+UseParallelGCor-XX:+UseParallelOldGC
  • Multi-cpu applications that seek low pause times and require fast response:-XX:+UseConcMarkSweepGC
parameter New generation garbage collector New generation algorithm Old age garbage collector Old age algorithm
-XX:+UseSerialGC SerialGC copy SerailOldGC Tag to sort out
-XX:+UseParNewGC ParNew copy SerailOldGC Tag to sort out
-XX:+UseParallelGC/-XX:+UseParallelOldGC Parallel Scavenge copy Parallel Old Tag to sort out
-XX:+UseConcMarkSweepGC ParNew copy CMS+Serial Old Mark clear

G1 collector

garbage-first heap+metaspace

Features of the G1 previous collector

  1. The young generation and the old generation are separate and contiguous blocks of memory
  2. The young generation uses the Eden + S0 + S1 replication algorithm
  3. Old age collection must scan the entire old age area
  4. Both are designed to perform GC as little as possible and as quickly as possible

G1 is introduced

G1 is a server-side garbage collector that can be used in multi-processor and large-memory environments to achieve high throughput while meeting garbage collection pause time requirements as much as possible. And has the following characteristics:

  • Like the CMS collector, can be executed concurrently with application threads
  • Decluttering free space is faster
  • More time is required to predict GC pause times
  • You don’t want to sacrifice a lot of throughput performance
  • A larger Java Heap is not required

G1 is designed to replace the CMS collector, as compared to CMS:

  • The G1 has a defragmenting process that doesn’t create a lot of memory fragmentation
  • The G1’s STW is more controllable, with a predictive mechanism for pause times that users can specify

The main change in G1 is that memory regions such as Eden, Survivor and Tenured are no longer contiguous, but become regions of the same size.

The characteristics of G1

  1. G1 can take full advantage of multi-CPU hardware and minimize STW
  2. G1 adopts the markup algorithm as a whole and the copy algorithm as a part, without memory fragmentation
  3. G1 divides memory into independent regions
  4. G1 logically preserves the old and new generations, but is no longer physically isolated, but a collection of regions (and does not require regions to be contiguous), and uses different GC methods to process different regions.
  5. G1 has only a logical concept of generation, with each partition potentially switching between generations as G1 runs.

Region

The size of each Region ranges from 1MB to 32MB, and a maximum of 2048 regions (2048 by default) can be configured. Therefore, the maximum memory supported by each Region is 64 GB.

G1 is still a generational collector. Some of these regions contain the new generation. The garbage of the new generation (Eden) is collected by STW and the surviving objects are copied to the old age or Survivor space. The other part of Region is old, and G1 cleans up by copying objects from one Region to another, so there is no CMS memory fragmentation problem.

In G1, there is also a special Region called the Humongous Region. If an object occupies more than 50% of the Region’s space, it is considered a giant object and allocated to the old age. G1 has a Humongous zone for giant objects. If a Humongous Region does not fit a single large object, G1 will look for contiguous Humongous regions to store. In order to find contiguous H regions, G1 will sometimes have to start Full GC.

Recycling process

Young GC: Collect for Eden area. If Eden area is exhausted, it will be penalized. It mainly collects small areas and forms continuous memory blocks to avoid memory fragmentation.

  • Data in Eden area is moved to Survivor area. If Survivor area has insufficient space, it is promoted to Old area
  • Survivor zone data is moved to the new Survivor zone and part of it is promoted to the Old zone
  • The GC ends, and the application continues

  1. Initial tag
  2. Concurrent tags
  3. In the end tag
  4. Filter collection: Maximize value collection based on time

Microservice production deployment and tuning optimization

The production server is slow

  1. Check the system status
    • usetopCommand to view the CPU usage, memory usage, and load Average in the first line
    • The three numbers following load Average record the system load averages of one minute, five minutes, and fifteen minutes, respectively
    • You can also useuptimeCommand is a simplified version of the system performance command
  2. Viewing CPU Information
    • usevmstat -n 2 3Represents sampling every 2 seconds, a total of 3 times
    • In the procs
      • R represents the number of processes running and waiting for the CPU time slice. The number of running queues in the entire system should not exceed 2 times of the total number of cores. Otherwise, the system is under too much pressure
      • B indicates the number of processes waiting for resources such as disk I/O and network I/O
    • The CPU
      • Us represents the percentage of CPU time consumed by the user process, and optimizes the program if it is longer than 50%
      • Sy represents the percentage of CPU time consumed by the kernel process
      • The us + SY reference value is 80%. If the sum is greater than 80%, the CPU may be insufficient
    • usempstat -P ALL 2View information about all CPU cores
    • usepidstat -u 1 -p pidView the CPU usage breakdown information of each process
  3. Viewing Memory Information
    • usefree -mTo view the memory, in MB
    • Application free memory/system physical memory should be between 20% and 70%
    • usePidstat -p pid -r Number of sampling seconds
  4. Viewing Disk Information
    • usedf -h
  5. Viewing Disk I/O
    • useiostat -xdk 2 3
      • RkB /s Data read per second
      • WkB /s Data written per second
      • SVCTM Average SERVICE time of I/O requests, expressed in ms
      • Util What percentage of a second is used for I/O operations
    • usePidstat -d Sampling interval in seconds -p PID
  6. Viewing Network IO
    • useIfstat Sampling interval in seconds

Excessively high CPU usage

  1. usetopCommand to find the process with the highest CPU usage
  2. ps -eforjpsFurther locate the process information
  3. Locate a specific thread or code
    • useps -mp pid -o THREAD,tid,time
      • -m Displays all threads
      • -p Indicates the CPU usage time of the PID process
      • -o indicates a user-defined format
  4. Convert the required thread ID to hexadecimal (lowercase)
  5. Jstack process id | grep tid - A60