• JVM优化第一天


    一、我们为什么要对jvm做优化?

    在本地开发环境中我们很少会遇到需要对jvm进行优化的需求,但是到了生产环境,我们可能将有下面的需求:

    1、运行的应用 “卡住了”,日志不输出,或者请求时,程序没有反应;

    2、服务器的 CPU负载突然升高

    3、在多线程应用下,如何合理分配线程的数量?

    在一个应用程序中,不是我们的线程分配越多,效率越高。

    我们要解决问题,而不是重启解决。

    我们不仅要让程序能跑起来,而且是可以跑的更快!可以分析解决在生产环境中所遇到的各种“棘手”的问题。

    说明:我们使用的jdk版本为1.8。

    二、jvm的运行参数以及参数的设置

    在jvm中有很多的参数可以进行设置,这样可以让jvm在各种环境中都能够高效的运行。绝大部分的参数保持默认即可。

    三种参数类型

    jvm的参数类型分为三类,分别是:

    (1)、标准参数

    -help
    -version

    (2)、-X 参数 (非标准参数)

    -Xint
    -Xcomp

    (3)、-XX 参数(使用率较高)

    -XX:newSize,初始年轻代大小,年轻代最大为maxNewSize。
    -XX:+UseSerialGC

    1、标准参数

    jvm的标准参数,一般都是很稳定的,在未来的JVM版本中不会改变,可以使用java -help检索出所有的标准参数。

    打开linux操作系统进行测试

    [root@VM]# java -help
    Usage: java [-options] class [args...]
               (to execute a class)
       or  java [-options] -jar jarfile [args...]
               (to execute a jar file)
    where options include:
        -d32      use a 32-bit data model if available
        -d64      use a 64-bit data model if available
        -server      to select the "server" VM
                      The default VM is server,
                      because you are running on a server-class machine.
    
    
        -cp <class search path of directories and zip/jar files>
        -classpath <class search path of directories and zip/jar files>
                      A : separated list of directories, JAR archives,
                      and ZIP archives to search for class files.
        -D<name>=<value>
                      set a system property
        -verbose:[class|gc|jni]
                      enable verbose output
        -version      print product version and exit
        -version:<value>
                      Warning: this feature is deprecated and will be removed
                      in a future release.
                      require the specified version to run
        -showversion  print product version and continue
        -jre-restrict-search | -no-jre-restrict-search
                      Warning: this feature is deprecated and will be removed
                      in a future release.
                      include/exclude user private JREs in the version search
        -? -help      print this help message
        -X            print help on non-standard options
        -ea[:<packagename>...|:<classname>]
        -enableassertions[:<packagename>...|:<classname>]
                      enable assertions with specified granularity
        -da[:<packagename>...|:<classname>]
        -disableassertions[:<packagename>...|:<classname>]
                      disable assertions with specified granularity
        -esa | -enablesystemassertions
                      enable system assertions
        -dsa | -disablesystemassertions
                      disable system assertions
        -agentlib:<libname>[=<options>]
                      load native agent library <libname>, e.g. -agentlib:hprof
                      see also, -agentlib:jdwp=help and -agentlib:hprof=help
        -agentpath:<pathname>[=<options>]
                      load native agent library by full pathname
        -javaagent:<jarpath>[=<options>]
                      load Java programming language agent, see java.lang.instrument
        -splash:<imagepath>
                      show splash screen with specified image
    See http://www.oracle.com/technetwork/java/javase/documentation/index.html for more details.

    1)、查看jvm版本

    [root@ ~]# java -version
    openjdk version "1.8.0_312"
    OpenJDK Runtime Environment (build 1.8.0_312-b07)
    OpenJDK 64-Bit Server VM (build 25.312-b07, mixed mode)

    # ‐showversion参数是表示,先打印版本信息,再执行后面的命令,在调试时非常有用,后面会使用到。

    2)、通过-D设置系统属性参数

    -D<name>=<value>

    我们写代码来测试一下

    public class TestJVM {
        public static void main(String[] args) {
            String str = System.getProperty("str"); // 获取系统参数
            if(str == null){ // 如果没有参数
                System.out.println("zwh");
            } else {
                System.out.println(str);
            }
        }
    }

    运行结果:zwh

    在根目录下创建test目录

    mkdir /test

    进入test目录

    cd /test

    创建一个TestJVM.java文件

    vim TestJVM.java

    将代码赋值进去,再退出来。

    使用javac命令进行编译

    javac TestJVM.java

    test目录下会生成.class文件

    [root@VM-0-10-centos test]# ll
    total 8
    -rw-r--r-- 1 root root 570 Jan  5 15:44 TestJVM.class
    -rw-r--r-- 1 root root 296 Jan  5 15:44 TestJVM.java

    执行.class文件

    java TestJVM

    结果如下:

    [root@VM-0-10-centos test]# java TestJVM
    zwh

    那我们如何设置参数呢?

    java -Dstr=hello TestJVM

    结果如下:

    [root@VM-0-10-centos test]# java -Dstr=hello TestJVM
    hello

    由于我们设置了str参数,那么我们就可以在代码中获取到参数并打印出来。

     3)、-server与-client参数

     可以通过-server或-client设置jvm的运行参数。

    它们的区别是 Server VM的初始堆空间会大一些,默认使用的是并行垃圾回收器,启动慢运行快

    Client VM 相对来讲会保守一些,初始堆空间会小一些,使用串行的垃圾回收器,它的目标是为了让JVM的启动速度更快,但运行速度会比Server模式慢些

    JVM 在启动的时候会根据硬件和操作系统自动选择使用Server还是Client类型的JVM。

    对于32 位操作系统

    (1)、如果是 Windows系统,不论硬件配置如何,都默认使用Client类型的JVM。

    (2)、如果是其他操作系统上,机器配置有 2GB以上的内存同时有2个以上CPU的话默认使用server模式,否则使用client模式。

    对于64 位操作系统:只有 server类型(并行垃圾回收器),不支持client类型。

    测试:当使用-client时,由于是64位操作系统,依然是server模式。

    [root@VM-0-10-centos test]# java -client -showversion TestJVM
    openjdk version "1.8.0_312"
    OpenJDK Runtime Environment (build 1.8.0_312-b07)
    OpenJDK 64-Bit Server VM (build 25.312-b07, mixed mode)
    
    zwh
    [root@VM-0-10-centos test]# java -server -showversion TestJVM
    openjdk version "1.8.0_312"
    OpenJDK Runtime Environment (build 1.8.0_312-b07)
    OpenJDK 64-Bit Server VM (build 25.312-b07, mixed mode)
    
    zwh

    -server 模式用得更多,很少用-client模式。

    2、-X参数

    jvm的-X参数是非标准参数,在不同版本的jvm中,参数可能会有所不同,可以通过java -X查看非标准参数。

    [root@VM-0-10-centos test]# java -X
        -Xmixed           mixed mode execution (default)
        -Xint             interpreted mode execution only
        -Xbootclasspath:<directories and zip/jar files separated by :>
                          set search path for bootstrap classes and resources
        -Xbootclasspath/a:<directories and zip/jar files separated by :>
                          append to end of bootstrap class path
        -Xbootclasspath/p:<directories and zip/jar files separated by :>
                          prepend in front of bootstrap class path
        -Xdiag            show additional diagnostic messages
        -Xnoclassgc       disable class garbage collection
        -Xincgc           enable incremental garbage collection
        -Xloggc:<file>    log GC status to a file with time stamps
        -Xbatch           disable background compilation
        -Xms<size>        set initial Java heap size
        -Xmx<size>        set maximum Java heap size
        -Xss<size>        set java thread stack size
        -Xprof            output cpu profiling data
        -Xfuture          enable strictest checks, anticipating future default
        -Xrs              reduce use of OS signals by Java/VM (see documentation)
        -Xcheck:jni       perform additional checks for JNI functions
        -Xshare:off       do not attempt to use shared class data
        -Xshare:auto      use shared class data if possible (default)
        -Xshare:on        require using shared class data, otherwise fail.
        -XshowSettings    show all settings and continue
        -XshowSettings:all
                          show all settings and continue
        -XshowSettings:vm
                          show all vm related settings and continue
        -XshowSettings:system
                          (Linux Only) show host system or container
                          configuration and continue
        -XshowSettings:properties
                          show all property settings and continue
        -XshowSettings:locale
                          show all locale related settings and continue
    
    The -X options are non-standard and subject to change without notice.

    1)、-Xint、-Xcomp、-Xmixed

    -Xmixed:默认执行混合模式

    1)、在解释模式 (interpreted mode)下,-Xint标记会强制JVM执行所有的字节码,当然这会降低运行速度,通常低10倍或更多。

    2)、-Xcomp 参数与它(-Xint)正好相反,JVM在第一次使用时会把所有的字节码编译成本地代码后再执行,会有卡顿再执行的现象,卡顿就是在将所有的字节码编译成本地代码,它的效率比解释模式要高,因为只用做一次编译,后面会运行比较快,从而带来最大程度的优化,而解释模式是每一次都要逐行执行,效率相对较低。

    然而,很多应用在使用 -Xcomp也会有一些性能损失,当然这比使用-Xint损失的少,原因是-xcomp没有让JVM启用JIT编译器的全部功能。JIT编译器可以对是否需要编译做判断,如果所有代码都进行编译的话,对于一些只执行一次的代码就没有意义了。

    -Xcomp牺牲了一部分JIP编译器的功能,JIP编译器会自动的判断是执行字节码还是编译成本地代码来执行,有些代码只需要执行一次就可以了,没有必要把执行一次的代码编译成本地代码。

    3)、-Xmixed 是混合模式,将解释模式与编译模式(compile mode)进行混合使用,由jvm自己决定是解释执行还是编译后再执行,这是jvm默认的模式,也是推荐使用的模式

    示例:强制设置运行模式

    强制设置为解释模式

    [root@VM-0-10-centos test]# java -showversion -Xint TestJVM
    openjdk version "1.8.0_312"
    OpenJDK Runtime Environment (build 1.8.0_312-b07)
    OpenJDK 64-Bit Server VM (build 25.312-b07, interpreted mode)
    
    zwh

    强制设置为编译模式

    [root@VM-0-10-centos test]# java -showversion -Xcomp TestJVM
    openjdk version "1.8.0_312"
    OpenJDK Runtime Environment (build 1.8.0_312-b07)
    OpenJDK 64-Bit Server VM (build 25.312-b07, compiled mode)
    
    zwh

    注意:编译模式下,第一次执行会比解释模式下执行慢一些,注意观察。

     默认的混合模式(sever模式),不用加参数。

    [root@VM-0-10-centos test]# java -showversion TestJVM
    openjdk version "1.8.0_312"
    OpenJDK Runtime Environment (build 1.8.0_312-b07)
    OpenJDK 64-Bit Server VM (build 25.312-b07, mixed mode)
    
    zwh

    3、-XX参数

    -XX 参数也是非标准参数,主要用于jvm的调优和debug操作

     -XX参数的使用有2种方式,一种是boolean类型,一种是非boolean类型:

    1)、boolean 类型

    格式: -XX:[+-]<name>表示启用或禁用<name>属性。+表示启用,-表示禁用

    如: -XX:+DisableExplicitGC 表示禁用手动调用gc操作,也就是说调用System.gc()无效。

    我们可以在代码中手动调用gc操作,

    public class TestJVM {
        public static void main(String[] args) {
            String str = System.getProperty("str");
            if(str == null){
                System.out.println("itcast");
            } else {
                System.out.println(str);
            }
            System.gc(); // 通知垃圾回收器进行回收,仅仅是通知,
        }
    }

    我们往往不希望程序员在代码中手动调用gc,而是由垃圾回收器自动做出判断。如果我们设置 -XX:+DisableExplicitGC,则手动调用System.gc()无效。如果设置-XX:-DisableExplicitGC,则手动调用gc操作会通知垃圾回收器进行垃圾回收。

    [root@VM-0-10-centos test]# java -showversion -XX:+DisableExplicitGC TestJVM
    openjdk version "1.8.0_312"
    OpenJDK Runtime Environment (build 1.8.0_312-b07)
    OpenJDK 64-Bit Server VM (build 25.312-b07, mixed mode)
    
    zwh

    加上-XX:+DisableExplicitGC参数后,代码中的System.gc()无效。

    2)、非 boolean类型

    格式: -XX:<name>=<value>表示<name>属性的值位<value>

    如: -XX:NewRatio=1 表示新生代和老年代的比值

    4、-Xms与-Xmx参数(属于-XX参数)

    -Xms与-Xmx分别是设置jvm的堆内存的初始大小和最大大小。设置堆的最大内存,其实是在做JVM的调优。

    -Xms512m:等价于-XX:InitialHeapSize,设置JVM初始堆内存为512M。

    -Xmx2048m:等价于-XX:MaxHeapSize,设置JVM最大堆内存为2048M(2G)。

    适当的调整jvm的内存大小,可以充分利用服务器资源,让程序跑的更快。

    示例:

    java ‐Xms512m ‐Xmx2048m TestJVM

    5、查看jvm的运行参数

    有些时候我们需要查看jvm的运行参数,这个需求可能会存在2种情况:

    第一,运行 java命令时打印出运行参数;

    第二,查看正在运行的java进程的参数;

    1)、运行java命令时打印参数

    运行java命令时打印参数,需要添加-XX:+PrintFlagsFinal参数即可。

    [root@VM-0-10-centos test]# java -XX:+PrintFlagsFinal TestJVM
    [Global flags]
         intx ActiveProcessorCount                      = -1                                  {product}
        uintx AdaptiveSizeDecrementScaleFactor          = 4                                   {product}
        uintx AdaptiveSizeMajorGCDecayTimeScale         = 10                                  {product}
        uintx AdaptiveSizePausePolicy                   = 0                                   {product}
        uintx AdaptiveSizePolicyCollectionCostMargin    = 50                                  {product}
        uintx AdaptiveSizePolicyInitializingSteps       = 20                                  {product}
        uintx AdaptiveSizePolicyOutputInterval          = 0                                   {product}
        uintx AdaptiveSizePolicyWeight                  = 10                                  {product}
        uintx AdaptiveSizeThroughPutPolicy              = 0                                   {product}
        uintx AdaptiveTimeWeight                        = 25                                  {product}
         bool AdjustConcurrency                         = false                               {product}
         bool AggressiveHeap                            = false                               {product}
         bool AggressiveOpts                            = false                               {product}
         intx AliasLevel                                = 3                                   {C2 product}
         bool AlignVector                               = true                                {C2 product}
         intx AllocateInstancePrefetchLines             = 1                                   {product}
         intx AllocatePrefetchDistance                  = 256                                 {product}
         intx AllocatePrefetchInstr                     = 3                                   {product}
         intx AllocatePrefetchLines                     = 3                                   {product}
         intx AllocatePrefetchStepSize                  = 64                                  {product}
         intx AllocatePrefetchStyle                     = 1                                   {product}
         bool AllowJNIEnvProxy                          = false                               {product}
         bool AllowNonVirtualCalls                      = false                               {product}
         bool AllowParallelDefineClass                  = false                               {product}
         bool AllowUserSignalHandlers                   = false                               {product}
         bool AlwaysActAsServerClassMachine             = false                               {product}
         bool AlwaysCompileLoopMethods                  = false                               {product}
         bool AlwaysLockClassLoader                     = false                               {product}
         bool AlwaysPreTouch                            = false                               {product}
         bool AlwaysRestoreFPU                          = false                               {product}
         bool AlwaysTenure                              = false                               {product}
         bool AssertOnSuspendWaitFailure                = false                               {product}
         bool AssumeMP                                  = true                                {product}
         intx AutoBoxCacheMax                           = 128                                 {C2 product}
        uintx AutoGCSelectPauseMillis                   = 5000                                {product}
         intx BCEATraceLevel                            = 0                                   {product}
         intx BackEdgeThreshold                         = 100000                              {pd product}
         bool BackgroundCompilation                     = true                                {pd product}
         …………………………略…………………………………………
          bool UseSquareToLenIntrinsic                   = true                                {C2 product}
         bool UseStoreImmI16                            = false                               {ARCH product}
         bool UseStringDeduplication                    = false                               {product}
         bool UseSuperWord                              = true                                {C2 product}
         bool UseTLAB                                   = true                                {pd product}
         bool UseThreadPriorities                       = true                                {pd product}
         bool UseTransparentHugePages                   = false                               {product}
         bool UseTypeProfile                            = true                                {product}
         bool UseTypeSpeculation                        = true                                {C2 product}
         bool UseUnalignedLoadStores                    = false                               {ARCH product}
         bool UseVMInterruptibleIO                      = false                               {product}
         bool UseXMMForArrayCopy                        = true                                {product}
         bool UseXmmI2D                                 = false                               {ARCH product}
         bool UseXmmI2F                                 = false                               {ARCH product}
         bool UseXmmLoadAndClearUpper                   = true                                {ARCH product}
         bool UseXmmRegToRegMoveAll                     = true                                {ARCH product}
         bool VMThreadHintNoPreempt                     = false                               {product}
         intx VMThreadPriority                          = -1                                  {product}
         intx VMThreadStackSize                         = 1024                                {pd product}
         intx ValueMapInitialSize                       = 11                                  {C1 product}
         intx ValueMapMaxLoopSize                       = 8                                   {C1 product}
         intx ValueSearchLimit                          = 1000                                {C2 product}
         bool VerifyMergedCPBytecodes                   = true                                {product}
         bool VerifySharedSpaces                        = false                               {product}
         intx WorkAroundNPTLTimedWaitHang               = 1                                   {product}
        uintx YoungGenerationSizeIncrement              = 20                                  {product}
        uintx YoungGenerationSizeSupplement             = 80                                  {product}
        uintx YoungGenerationSizeSupplementDecay        = 8                                   {product}
        uintx YoungPLABSize                             = 4096                                {product}
         bool ZeroTLAB                                  = false                               {product}
         intx hashCode                                  = 5                                   {product}
    zwh

    由上述的信息可以看出,参数有 boolean类型和数字类型,值的操作符是=或:=,分别代表默认值和被修改的值。

    示例:

    [root@VM-0-10-centos test]# java -XX:+PrintFlagsFinal -XX:+VerifySharedSpaces TestJVM
    [Global flags]
         intx ActiveProcessorCount                      = -1                                  {product}
        uintx AdaptiveSizeDecrementScaleFactor          = 4                                   {product}
        uintx AdaptiveSizeMajorGCDecayTimeScale         = 10                                  {product}
        uintx AdaptiveSizePausePolicy                   = 0                                   {product}
        uintx AdaptiveSizePolicyCollectionCostMargin    = 50                                  {product}
        uintx AdaptiveSizePolicyInitializingSteps       = 20                                  {product}
        uintx AdaptiveSizePolicyOutputInterval          = 0                                   {product}
        uintx AdaptiveSizePolicyWeight                  = 10                                  {product}
        uintx AdaptiveSizeThroughPutPolicy              = 0                                   {product}
        uintx AdaptiveTimeWeight                        = 25                                  {product}
         bool AdjustConcurrency                         = false                               {product}
         bool AggressiveHeap                            = false                               {product}
         bool AggressiveOpts                            = false                               {product}
         intx AliasLevel                                = 3                                   {C2 product}
         bool AlignVector                               = true                                {C2 product}
         intx AllocateInstancePrefetchLines             = 1                                   {product}
         intx AllocatePrefetchDistance                  = 256                                 {product}
         intx AllocatePrefetchInstr                     = 3                                   {product}
         intx AllocatePrefetchLines                     = 3                                   {product}
         intx AllocatePrefetchStepSize                  = 64                                  {product}
         intx AllocatePrefetchStyle                     = 1                                   {product}
         bool AllowJNIEnvProxy                          = false                               {product}
         bool AllowNonVirtualCalls                      = false                               {product}
         bool AllowParallelDefineClass                  = false                               {product}
         …………………………略…………………………………………
         bool UseSquareToLenIntrinsic                   = true                                {C2 product}
         bool UseStoreImmI16                            = false                               {ARCH product}
         bool UseStringDeduplication                    = false                               {product}
         bool UseSuperWord                              = true                                {C2 product}
         bool UseTLAB                                   = true                                {pd product}
         bool UseThreadPriorities                       = true                                {pd product}
         bool UseTransparentHugePages                   = false                               {product}
         bool UseTypeProfile                            = true                                {product}
         bool UseTypeSpeculation                        = true                                {C2 product}
         bool UseUnalignedLoadStores                    = false                               {ARCH product}
         bool UseVMInterruptibleIO                      = false                               {product}
         bool UseXMMForArrayCopy                        = true                                {product}
         bool UseXmmI2D                                 = false                               {ARCH product}
         bool UseXmmI2F                                 = false                               {ARCH product}
         bool UseXmmLoadAndClearUpper                   = true                                {ARCH product}
         bool UseXmmRegToRegMoveAll                     = true                                {ARCH product}
         bool VMThreadHintNoPreempt                     = false                               {product}
         intx VMThreadPriority                          = -1                                  {product}
         intx VMThreadStackSize                         = 1024                                {pd product}
         intx ValueMapInitialSize                       = 11                                  {C1 product}
         intx ValueMapMaxLoopSize                       = 8                                   {C1 product}
         intx ValueSearchLimit                          = 1000                                {C2 product}
         bool VerifyMergedCPBytecodes                   = true                                {product}
         bool VerifySharedSpaces                       := true                                {product}
         intx WorkAroundNPTLTimedWaitHang               = 1                                   {product}
        uintx YoungGenerationSizeIncrement              = 20                                  {product}
        uintx YoungGenerationSizeSupplement             = 80                                  {product}
        uintx YoungGenerationSizeSupplementDecay        = 8                                   {product}
        uintx YoungPLABSize                             = 4096                                {product}
         bool ZeroTLAB                                  = false                               {product}
         intx hashCode                                  = 5                                   {product}
    zwh

    可以看到VerifySharedSpaces这个参数已经被修改了。

    2)、查看正在运行的jvm参数

    如果想要查看正在运行的jvm就需要借助于jinfo命令查看。

    首先,启动一个 tomcat用于测试,tomcat是Java写的一个应用,来观察下运行的jvm参数。通过rz命令上传。建议用Filezilla传输,速度很快。

    cd /test/
     rz 上传
     tar ‐xvf apache‐tomcat‐7.0.57.tar.gz
     cd apache‐tomcat‐7.0.57
     cd bin/
     ./startup.sh

    http://192.168.40.133:8080/ 进行访问

     访问成功:

    由于我的服务器已经启动了两个tomcat,我就不再重新启动tomcat了。

    查看所有的参数,用法:jinfo ‐flags <进程id>

    通过ps -ef | grep tomcat获取进程ID,或通过jdk为我们提供的命令jps 或者  jps ‐l 查看java进程,

    [root@VM-0-10-centos conf]# jps
    4784 Bootstrap
    10274 Bootstrap
    12078 Jps

    发现有三个进程,4784和10274就是tomcat,显示的是Bootstrap。12078是jps,即当前命令本身。

     当执行jps -l命令

    [root@VM-0-10-centos conf]# jps -l
    4784 org.apache.catalina.startup.Bootstrap
    10274 org.apache.catalina.startup.Bootstrap
    12986 sun.tools.jps.Jps

    这样就能清楚的知道Bootstrap为tomcat的进程。

    输入命令:jinfo ‐flags 4784

    [root@VM-0-10-centos conf]# jinfo -flags 4784
    Attaching to process ID 4784, please wait...
    Debugger attached successfully.
    Server compiler detected.
    JVM version is 25.312-b07
    Non-default VM flags: -XX:CICompilerCount=2 -XX:InitialHeapSize=62914560 -XX:MaxHeapSize=994050048 -XX:MaxNewSize=331350016 
    -XX:MinHeapDeltaBytes=524288 -XX:NewSize=20971520 -XX:OldSize=41943040 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops
    -XX:+UseParallelGC Command line: -Djava.util.logging.config.file=/root/tomcat-server/conf/logging.properties
    -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djdk.tls.ephemeralDHKeySize=2048
    -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Dorg.apache.catalina.security.SecurityListener.UMASK=0027
    -Dignore.endorsed.dirs= -Dcatalina.base=/root/tomcat-server -Dcatalina.home=/root/tomcat-server
    -Djava.io.tmpdir=/root/tomcat-server/temp

    -XX:MaxHeapSize=994050048中,最大堆内存的单位为字节,为948M。

     查看某一参数的值,用法:jinfo ‐flag <参数名> <进程id>

    [root@VM-0-10-centos conf]# jinfo -flag MaxHeapSize 4784
    -XX:MaxHeapSize=994050048

    注意:flags查询所有参数,如果查询单个参数用flag。

    三、jvm的内存模型(堆内存)

    jvm的内存模型在1.7和1.8有较大的区别,虽然本套课程是以1.8为例进行讲解,但是我们也是需要对1.7的内存模型有所了解,所以接下里,我们将先学习1.7再学习1.8的内存模型。

    1、jdk1.7的堆内存模型

     

     Young 年轻区(代)

    Young区被划分为三部分,Eden(伊甸园)区和两个大小严格相同的Survivor区,其中,Survivor区间中,某一时刻只有其中一个是被使用的,另外一个留做垃圾收集时复制对象用,在Eden区间变满的时候, GC就会将存活的对象移到空闲的Survivor区间中,根据JVM的策略,在经过几次垃圾收集后,任然存活于Survivor的对象将被移动到Tenured区间。

    当我们new一个对象的时候,首先我们会把对象放到eden区,当eden区变满之后,我们就会把存活的对象转移到survivor中,垃圾对象就会被回收掉。如果survivor中的对象经过几次垃圾回收后依然存货的将被转移到Tenured区。1.8中同样是这样。

    注意:并不是所有new的对象都会放到eden区,如果new的一个对象不是大对象,就会进入年轻代,如果你new的一个对象是大对象,就不会进入年轻代,直接进入老年区。

    Tenured 年老区

    Tenured区主要保存生命周期长的对象,一般是一些老的对象,当一些对象在Young复制转移一定的次数以后,对象就会被转移到Tenured区,一般如果系统中用了application级别的缓存,缓存中的对象往往会被转移到这一区间。

    并不是说只要把对象转移到年老区就能永久的保存下来,而是在老年代中也可能被回收。

    Perm 永久区

    Perm代主要保存class,method,filed对象,这部份的空间一般不会溢出,除非一次性加载了很多的类,不过在涉及到热部署的应用服务器的时候,有时候会遇到java.lang.OutOfMemoryError : PermGen space 的错误,造成这个错误的很大原因就有可能是每次都重新部署,但是重新部署后,类的class没有被卸载掉,这样就造成了大量的class对象保存在了perm中,这种情况下,一般重新启动应用服务器可以解决问题。

    当我们启动了一个tomcat,tomcat中部署了我们的应用,tomcat想要把我们的应用跑起来,一定会加载应用中的class类和接口,method和filed对象就会保存到永久区中。

    Virtual 区

    最大内存和初始内存的差值,就是 Virtual区。

    2、jdk1.8的堆内存模型

     

    由上图可以看出, jdk1.8的内存模型是由2部分组成,年轻代 + 年老代。

    年轻代:Eden + 2*Survivor

    年老代:OldGen

    在jdk1.8中变化最大的Perm区,用Metaspace(元数据空间)进行了替换。

    需要特别说明的是:Metaspace所占用的内存空间不是在Java虚拟机内部,而是在本地服务器内存空间中,这也是与1.7的永久代最大的区别所在。

    1.8的内存模型如下:内存分为两块:堆内存和非堆内存。非堆内存指的就是metaspace。非堆内存主要有两块空间,CodeCache存放类class,CCS代表类的压缩指针,如果我们设置压缩指针开启的话,就会使用这个区域,否则不使用。

    3、为什么要废弃1.7中的永久区?

    This is part of the JRockit and Hotspot convergence effort. JRockit customers do not need to configure the permanent generation 
    (since JRockit does not have a permanent generation) and are accustomed to not
    configuring the permanent generation.
    移除永久代是为融合HotSpot JVM与 JRockit VM而做出的努力,因为JRockit没有永久代, 不需要配置永久代。

    现实使用中,由于永久代内存经常不够用或发生内存泄露,爆出异常java.lang.OutOfMemoryError: PermGen。基于此,将永久区废弃,而改用元空间,改为了使用本地内存空间。

     我们对永久代进行调整的时候,如果分配得多,有些资源用不了,就会产生资源得浪费,如果分配得少,又不够用。

    4、通过jstat命令进行查看堆内存使用情况

     jstat命令可以查看堆内存各部分的使用量,以及加载类的数量。

    命令的格式如下:

    jstat [-命令选项] [vmid] [间隔时间/毫秒] [查询次数]

    vmid是进程id。通过jps查到进程id

    [root@VM-0-10-centos ~]# jps
    4784 Bootstrap
    10274 Bootstrap
    1390 Jps

    1)、查看class加载统计

    [root@VM-0-10-centos ~]# jstat -class 4784
    Loaded  Bytes  Unloaded  Bytes     Time   
     13876 26865.0        0     0.0       7.66

    说明:

    Loaded :加载class的数量,总共加载了13876个class类。

    Bytes :所占用空间大小,Unloaded :未加载数量,Bytes :未加载占用空间,Time :加载需要的时间。
    2)、查看编译统计

    [root@VM-0-10-centos ~]# jstat -compiler 4784
    Compiled Failed Invalid   Time   FailedType FailedMethod
       14600      2       0    40.81          1 org/springframework/cglib/core/MethodWrapper$MethodWrapperKey$$KeyFactoryByCGLIB$$552be97a hashCode

    说明:
    Compiled :编译数量。Failed :失败数量,Invalid :不可用数量,Time :编译需要的时间,FailedType :失败类型,FailedMethod :失败的方法。

    3)、垃圾回收统计(用得多一些)

    [root@VM-0-10-centos ~]# jstat -gc 4784
     S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT   
    2560.0 2560.0  0.0   1978.6 27648.0  17766.6   138240.0   96376.4   87168.0 83814.3 10112.0 9511.9    207    1.884   4      0.569    2.452

    也可以指定打印的间隔和次数,每1秒中打印一次,共打印5次

    [root@VM-0-10-centos ~]# jstat -gc 4784 1000 5
     S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT   
    2560.0 2560.0  0.0   1978.6 27648.0  18039.7   138240.0   96376.4   87168.0 83814.3 10112.0 9511.9    207    1.884   4      0.569    2.452
    2560.0 2560.0  0.0   1978.6 27648.0  18039.7   138240.0   96376.4   87168.0 83814.3 10112.0 9511.9    207    1.884   4      0.569    2.452
    2560.0 2560.0  0.0   1978.6 27648.0  18039.7   138240.0   96376.4   87168.0 83814.3 10112.0 9511.9    207    1.884   4      0.569    2.452
    2560.0 2560.0  0.0   1978.6 27648.0  18039.7   138240.0   96376.4   87168.0 83814.3 10112.0 9511.9    207    1.884   4      0.569    2.452
    2560.0 2560.0  0.0   1978.6 27648.0  18039.7   138240.0   96376.4   87168.0 83814.3 10112.0 9511.9    207    1.884   4      0.569    2.452

    说明:C表示capacity容量,U表示used。
    S0C :第一个Survivor区的大小(KB),

    S1C :第二个Survivor区的大小(KB),

    S0U :第一个Survivor区的使用大小(KB),

    S1U :第二个Survivor区的使用大小(KB),

    EC :Eden区的大小(KB),

    EU :Eden区的使用大小(KB),

    OC :Old区大小(KB),

    OU :Old使用大小(KB),

    MC :方法method区大小(KB),

    MU :方法区使用大小(KB),

    CCSC :压缩类空间大小(KB),

    CCSU :压缩类空间使用大小(KB),

    YGC :年轻代垃圾回收次数,

    YGCT :年轻代垃圾回收消耗时间,

    FGC :老年代垃圾回收次数,

    FGCT :老年代垃圾回收消耗时间,

    GCT :垃圾回收消耗总时间。

    四、 jmap命令的使用以及通过MAT工具进行分析

     前面通过jstat可以对jvm堆的内存进行统计分析,而jmap可以获取到更加详细的内容,如:内存使用情况的汇总、对内存溢出的定位与分析。

    1、查看内存使用情况

    命令:jmap -heap <pid>

    [root@VM-0-10-centos ~]# jmap -heap 4784
    Attaching to process ID 4784, please wait...
    Debugger attached successfully.
    Server compiler detected.
    JVM version is 25.312-b07
    
    using thread-local object allocation.
    Parallel GC with 2 thread(s)
    
    Heap Configuration: # 堆内存配置信息
       MinHeapFreeRatio         = 0
       MaxHeapFreeRatio         = 100
       MaxHeapSize              = 994050048 (948.0MB)  // 最大堆内存
       NewSize                  = 20971520 (20.0MB) // 年轻代初始大小
       MaxNewSize               = 331350016 (316.0MB) // 最大年轻代的大小
       OldSize                  = 41943040 (40.0MB)
       NewRatio                 = 2
       SurvivorRatio            = 8
       MetaspaceSize            = 21807104 (20.796875MB)
       CompressedClassSpaceSize = 1073741824 (1024.0MB)
       MaxMetaspaceSize         = 17592186044415 MB
       G1HeapRegionSize         = 0 (0.0MB)
    
    Heap Usage: # 堆内存使用情况
    PS Young Generation
    Eden Space:
       capacity = 28311552 (27.0MB)
       used     = 26161960 (24.949989318847656MB)
       free     = 2149592 (2.0500106811523438MB)
       92.40736784758391% used
    From Space: // survivor区
       capacity = 2621440 (2.5MB)
       used     = 2026064 (1.9322052001953125MB)
       free     = 595376 (0.5677947998046875MB)
       77.2882080078125% used
    To Space: // survivor区
       capacity = 2621440 (2.5MB)
       used     = 0 (0.0MB)
       free     = 2621440 (2.5MB)
       0.0% used
    PS Old Generation
       capacity = 141557760 (135.0MB)
       used     = 98689440 (94.11758422851562MB)
       free     = 42868320 (40.882415771484375MB)
       69.71672905815973% used
    
    41093 interned Strings occupying 4118040 bytes.

     2、查看内存中对象数量及大小

    查看所有对象,包括活跃以及非活跃的

    jmap ‐histo <pid> | more

    由于输出的对象比较多,我们用管道进行查看,按空格键继续往下翻页。

    查看活跃对象

    jmap ‐histo:live <pid> | more

    如:

    [root@VM-0-10-centos ~]# jmap -histo:live 4784 | more
    
     num     #instances         #bytes  class name
    ----------------------------------------------
       1:        250594       22752328  [C
       2:        250109        6002616  java.lang.String # 字符串对象有250109个,占用的空间为6002616
       3:         63615        5598120  java.lang.reflect.Method
       4:         74735        4394152  [Ljava.lang.Object;
       5:        108484        3471488  java.util.concurrent.ConcurrentHashMap$Node
       6:         80195        3207800  java.util.LinkedHashMap$Entry
       7:         31721        2463456  [Ljava.util.HashMap$Node;
       8:          7940        2457632  [B
       9:         73288        2345216  java.util.HashMap$Node
      10:         93546        2245104  java.util.ArrayList
      11:         33406        1870736  java.util.LinkedHashMap
      12:         14633        1644024  java.lang.Class
      13:         32320        1551360  org.aspectj.weaver.reflect.ShadowMatchImpl
      14:         32320        1034240  org.aspectj.weaver.patterns.ExposedState
      15:         20084         964032  java.util.HashMap
      16:           725         917760  [Ljava.util.concurrent.ConcurrentHashMap$Node;
      17:         20410         653120  java.lang.ref.WeakReference
      18:         26982         647568  org.apache.ibatis.scripting.xmltags.IfSqlNode
      19:         28957         636888  [Ljava.lang.Class;
      20:         37839         605424  org.apache.ibatis.scripting.xmltags.MixedSqlNode
      21:         37643         602288  org.apache.ibatis.scripting.xmltags.StaticTextSqlNode
      22:         23795         571080  java.util.Collections$UnmodifiableRandomAccessList
      23:         19340         489744  [Ljava.lang.String;
      24:         29918         478688  org.apache.ibatis.scripting.xmltags.ExpressionEvaluator
      25:         19854         476496  org.springframework.core.MethodClassKey
      26:         11077         443080  java.lang.ref.SoftReference
      27:          5814         418608  java.lang.reflect.Field
      28:          5673         365432  [I
      29:          4933         355176  org.springframework.core.annotation.AnnotationAttributes
      30:         19930         318880  java.lang.Object
      31:          2972         285312  org.apache.ibatis.mapping.MappedStatement
      32:         11248         269952  org.apache.catalina.loader.ResourceEntry
      33:          3916         219296  java.beans.MethodDescriptor
      34:         13655         218480  java.util.LinkedHashSet
      35:          6806         217792  springfox.documentation.schema.ModelRef
      36:          2431         213928  springfox.documentation.service.Parameter
      37:          2857         205704  org.apache.ibatis.mapping.ResultMapping
      38:          3020         193280  org.apache.ibatis.mapping.ResultMap
      39:          5958         190656  java.util.LinkedList
      40:         11590         185440  java.util.HashSet
    --More--

    对象说明:

    B  byte,[B表示字节数组,B为Byte的首字母。
    C  char,[C表示字符数组,C为Char的首字母
    D  double
    F  float
    I  int
    J  long
    Z  boolean
    [  数组,如[I表示int[]
    [L+类名 其他对象

    3、将内存使用情况dump到文件中

    有些时候我们需要将jvm当前内存中的情况dump到文件中,然后对它进行分析,jmap也是支持dump到文件中的。

    用法:b表示二进制binary

    jmap ‐dump:format=b,file=dumpFileName <pid>

    示例

    [root@VM-0-10-centos /]# jmap -dump:format=b,file=/test/dump.dat 4784
    Dumping heap to /test/dump.dat ...

    经过一段时间后

    [root@VM-0-10-centos /]# jmap -dump:format=b,file=/test/dump.dat 4784
    Dumping heap to /test/dump.dat ...
    Heap dump file created

    进入test目录查看

    [root@VM-0-10-centos test]# ll
    total 145716
    -rw------- 1 root root 149204696 Jan  6 20:59 dump.dat
    -rw-r--r-- 1 root root       570 Jan  5 15:44 TestJVM.class
    -rw-r--r-- 1 root root       296 Jan  5 15:44 TestJVM.java

    可以看到已经在 /test下生成了dump.dat的文件。后面将根据dump文件进行分析。

     4、通过jhat对dump文件进行分析

    在上一小节中,我们将jvm的内存dump到文件中,这个文件是一个二进制的文件,不方便查看,这时我们可以借助于jhat工具进行查看。

    用法:端口是自己随意指定的

    jhat ‐port <port> <file>

    注意:云服务器要放开9999端口,不然浏览器无法访问。由于我的云服务器放开了8080端口而没有放开9999端口,故

    [root@VM-0-10-centos ~]# jhat -port 8080 /test/dump.dat
    -bash: jhat -port 8080 /test/dump.dat: No such file or directory
    [root@VM-0-10-centos ~]# jhat -port 8080 /test/dump.dat
    Reading from /test/dump.dat...
    Dump file created Thu Jan 06 22:18:34 CST 2022
    Snapshot read, resolving...
    Resolving 2329083 objects...
    Chasing references, expect 465 dots..............................................................................................
    Eliminating duplicate references.................................................................................................
    Snapshot resolved.
    Started HTTP server on port 8080
    Server is ready.

    浏览器访问:http://IP:8080/,效果如下:

    会以包名进行分类,

     在最后面有 OQL查询功能。

    OQL:Object Query Languge对象查询语言。点击进去,效果如下:

    我们可以根据对象查询语句进行查询,点击OQL Help,效果如下:

     查询长度大于等于10000的字符串

    select s from java.lang.String s where s.value.length >= 10000

    将该语句拷贝到输入框中,点击执行

    效果如下:

    检查大对象:当一个对象很大的时候,我们要判断对象这么大是否合理,如果不合理,我们要找到这个对象所在的位置,并作出调整。

    点击进去查看内容:

    5、通过MAT工具对dump文件进行分析

    更常用,且功能更加强大。

     MAT工具介绍

    MAT(Memory Analyzer Tool),一个基于Eclipse的内存分析工具,是一个快速、功能丰富的JAVA heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗。使用内存分析工具从众多的对象中进行分析,快速的计算出在内存中对象的占用大小,看看是谁阻止了垃圾收集器的回收工作,并可以通过报表直观的查看到可能造成这种结果的对象。

    官网地址:https://www.eclipse.org/mat/

    MAT的下载和安装参考:https://www.cnblogs.com/zwh0910/p/15774590.html

    先把dump后的文件保存到本地,D:\install\MemoryAnalyzer\目录下。现在用MAT工具打开改文件,并进行分析。

    File→open Heap dump→选择dump.dat→Leak Suspects Report内存泄漏报表→finish

    Actions是重点:

      Histogram列出内存中的对象,对象的个数以及大小。

      Dominator Tree列出最大的对象以及其依赖存活的对象。

    点击Histogram(每个类的实例数)

     

    还可以输入内容进行查找对象

    点击Actons中的Dominator tree,进入如下界面

    可以查看对象间的依赖关系。

    点击进入报表界面,查看可能存在内存泄露的分析:

    还有列出有嫌疑的问题:

    五、定位分析内存溢出的方法

    内存溢出在实际的生产环境中经常会遇到,比如,不断的将数据写入到一个集合中出现了死循环读取超大的文件等等,都可能会造成内存溢出。

    如果出现了内存溢出,首先我们需要定位到发生内存溢出的环节,并且进行分析,是正常还是非正常情况,如果是正常的需求,就应该考虑加大内存的设置,如果是非正常需求,那么就要对代码进行修改,修复这个bug。

    首先,我们得先学会如何定位问题,然后再进行分析。如何定位问题呢,我们需要借助于jmap与MAT工具进行定位分析。通过jmap命令将堆内存的使用情况dump到一个文件中,再结合MAT工具对其进行分析,这样就能定位出内存溢出的位置,再解决问题。

    接下来,我们模拟内存溢出的场景。

    1、模拟内存溢出

    编写代码,向List集合中添加1000万个字符串,每个字符串由1000个UUID组成。如果程序能够正常执行,最后打印ok。如果不能正常执行则抛出异常。

    public class TestJvmOutOfMemory {
        public static void main(String[] args){
            List<Object> list = new ArrayList<>();
            for(int i=0; i<10000000; i++){
                String str="";
                for(int j=0; j<1000; j++){
                    str += UUID.randomUUID().toString();
                }
                list.add(str);
            }
            System.out.println("ok");
        }
    }

    为了演示效果,我们将设置执行的参数,这里使用的是Idea编辑器。

    设置运行参数

    ‐Xms8m ‐Xmx8m ‐XX:+HeapDumpOnOutOfMemoryError

    HeapDumpOnOutOfMemoryError:表示当发生内存溢出的时候,会把当前的堆内存dump到一个文件中,以便于后面的分析。

    设置为8m是为了让它快速抛出内存溢出的异常。

    结果如下:

    java.lang.OutOfMemoryError: Java heap space
    Dumping heap to java_pid20240.hprof ...
    Heap dump file created [8385977 bytes in 0.015 secs]
    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at java.util.Arrays.copyOf(Arrays.java:3332)
        at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
        at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448)
        at java.lang.StringBuilder.append(StringBuilder.java:136)
        at com.ccb.jawa.rest.TestJvmOutOfMemory.main(TestJvmOutOfMemory.java:18)

    可以看到,当发生内存溢出时,会dump文件到java_pid20240.hprof,在项目根目录下能找到改文件

    2、导入到MAT工具中进行分析

    嫌疑问题一:main线程中本地变量超过了78.8%,实例在java.lang.Object[]中。Array List的底层是Object[]。

     

    我们还可以点击details查看详情

    如果存在某一个线程占用了80%或90%的内存的时候,那么这个线程一定是有问题的。

    六、 jstack命令的使用

    有些时候我们需要查看下jvm中的线程执行情况,比如,发现服务器的CPU的负载突然增高了、出现了死锁死循环等,我们该如何分析呢?

    由于程序是正常运行的,没有任何的输出,从日志方面也看不出什么问题,所以就需要看下jvm的内部线程的执行情况,然后再进行分析查找出原因。 

    这个时候,就需要借助于jstack命令了,jstack的作用是将正在运行的jvm的线程情况进行快照,并且打印出来:

    1、输出每一个线程的执行情况

    用法:jstack <pid>

    [root@VM-0-10-centos bin]# jstack 21154
    2022-01-07 21:17:05
    Full thread dump OpenJDK 64-Bit Server VM (25.312-b07 mixed mode):
    
    "logback-1" #64 daemon prio=5 os_prio=0 tid=0x00007f5af0123000 nid=0xdbb waiting on condition [0x00007f5ae2ffd000]
       java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000000fe576c90> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1081)
        at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
        at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)
    
    "Attach Listener" #41 daemon prio=9 os_prio=0 tid=0x00007f5b0c00d800 nid=0x54b6 waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE  
    
    "http-nio-8081-exec-10" #40 daemon prio=5 os_prio=0 tid=0x00007f5b0c00a800 nid=0x53b0 waiting on condition [0x00007f5ae1e09000]
       java.lang.Thread.State: WAITING (parking)  # 线程http-nio-8081-exec-10处于waiting状态
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000000fe577330> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:146)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1112)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1176)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:748)
    
    "http-nio-8081-exec-9" #39 daemon prio=5 os_prio=0 tid=0x00007f5b0c009000 nid=0x53af waiting on condition [0x00007f5ae1f0a000]
       java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000000fe577330> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:146)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1112)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1176)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:748)
    
    "http-nio-8081-exec-8" #38 daemon prio=5 os_prio=0 tid=0x00007f5b0c007000 nid=0x53ae waiting on condition [0x00007f5ae200b000]
       java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000000fe577330> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:146)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1112)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1176)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:748)
    
    "http-nio-8081-exec-7" #37 daemon prio=5 os_prio=0 tid=0x00007f5afc599000 nid=0x53ad waiting on condition [0x00007f5ae210c000]
       java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000000fe577330> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:146)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1112)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1176)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:748)
    
    "http-nio-8081-exec-6" #36 daemon prio=5 os_prio=0 tid=0x00007f5afd6c7000 nid=0x53ac waiting on condition [0x00007f5ae220d000]
       java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000000fe577330> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:146)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1112)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1176)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:748)
    
    "http-nio-8081-exec-5" #35 daemon prio=5 os_prio=0 tid=0x00007f5afd40a800 nid=0x53a7 waiting on condition [0x00007f5ae250e000]
       java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000000fe577330> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:146)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1112)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1176)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:748)
    
    "http-nio-8081-exec-4" #34 daemon prio=5 os_prio=0 tid=0x00007f5b0c005000 nid=0x53a6 waiting on condition [0x00007f5ae260f000]
       java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000000fe577330> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:146)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1112)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1176)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:748)
    
    "http-nio-8081-exec-3" #33 daemon prio=5 os_prio=0 tid=0x00007f5b0c003800 nid=0x53a5 waiting on condition [0x00007f5ae2710000]
       java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000000fe577330> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:146)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1112)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1176)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:748)
    
    "http-nio-8081-exec-2" #32 daemon prio=5 os_prio=0 tid=0x00007f5b0c002800 nid=0x53a4 waiting on condition [0x00007f5ae2a11000]
       java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000000fe577330> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:146)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1112)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1176)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:748)
    
    "HikariPool-1 housekeeper" #30 daemon prio=5 os_prio=0 tid=0x00007f5adc8e2000 nid=0x5383 waiting on condition [0x00007f5b1f5e0000]
       java.lang.Thread.State: TIMED_WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000000fe6abbd8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
        at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1093)
        at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
        at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)
    
    "http-nio-8081-exec-1" #29 daemon prio=5 os_prio=0 tid=0x00007f5b0c001800 nid=0x5382 waiting on condition [0x00007f5b1f6e1000]
       java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000000fe577330> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:146)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1112)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1176)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:748)
    
    "http-nio-8081-AsyncTimeout" #27 daemon prio=5 os_prio=0 tid=0x00007f5b38196000 nid=0x52e5 waiting on condition [0x00007f5b1f1dc000]
       java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at org.apache.coyote.AbstractProtocol$AsyncTimeout.run(AbstractProtocol.java:1195)
        at java.lang.Thread.run(Thread.java:748)
    
    "http-nio-8081-Acceptor-0" #26 daemon prio=5 os_prio=0 tid=0x00007f5b38194000 nid=0x52e4 runnable [0x00007f5b1f2dd000]
       java.lang.Thread.State: RUNNABLE
        at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method)
        at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:421)
        at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:249)
        - locked <0x00000000c4d96718> (a java.lang.Object)
        at org.apache.tomcat.util.net.NioEndpoint$Acceptor.run(NioEndpoint.java:514)
        at java.lang.Thread.run(Thread.java:748)
    
    "http-nio-8081-ClientPoller-1" #25 daemon prio=5 os_prio=0 tid=0x00007f5b38192800 nid=0x52e3 runnable [0x00007f5b1f3de000]
       java.lang.Thread.State: RUNNABLE
        at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
        at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:269)
        at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:93)
        at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)
        - locked <0x00000000fe66b6e8> (a sun.nio.ch.Util$3)
        - locked <0x00000000fe66b6d8> (a java.util.Collections$UnmodifiableSet)
        - locked <0x00000000fe66b490> (a sun.nio.ch.EPollSelectorImpl)
        at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)
        at org.apache.tomcat.util.net.NioEndpoint$Poller.run(NioEndpoint.java:871)
        at java.lang.Thread.run(Thread.java:748)
    
    "http-nio-8081-ClientPoller-0" #24 daemon prio=5 os_prio=0 tid=0x00007f5b38192000 nid=0x52e2 runnable [0x00007f5b1f4df000]
       java.lang.Thread.State: RUNNABLE
        at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
        at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:269)
        at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:93)
        at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)
        - locked <0x00000000fe6a2c90> (a sun.nio.ch.Util$3)
        - locked <0x00000000fe6a2c80> (a java.util.Collections$UnmodifiableSet)
        - locked <0x00000000fe6a2a38> (a sun.nio.ch.EPollSelectorImpl)
        at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)
        at org.apache.tomcat.util.net.NioEndpoint$Poller.run(NioEndpoint.java:871)
        at java.lang.Thread.run(Thread.java:748)
    
    "ContainerBackgroundProcessor[StandardEngine[Catalina]]" #23 daemon prio=5 os_prio=0 tid=0x00007f5b38227800 nid=0x52e1 waiting on condition [0x00007f5b1c162000]
       java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.java:1363)
        at java.lang.Thread.run(Thread.java:748)
    
    "lettuce-nioEventLoop-4-2" #21 daemon prio=5 os_prio=0 tid=0x00007f5aec040000 nid=0x52e0 runnable [0x00007f5b002ba000]
       java.lang.Thread.State: RUNNABLE
        at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
        at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:269)
        at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:93)
        at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)
        - locked <0x00000000c72b4c10> (a io.netty.channel.nio.SelectedSelectionKeySet)
        - locked <0x00000000c72b4d00> (a java.util.Collections$UnmodifiableSet)
        - locked <0x00000000c72b4c28> (a sun.nio.ch.EPollSelectorImpl)
        at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)
        at io.netty.channel.nio.SelectedSelectionKeySetSelector.select(SelectedSelectionKeySetSelector.java:62)
        at io.netty.channel.nio.NioEventLoop.select(NioEventLoop.java:737)
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:392)
        at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:884)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.lang.Thread.run(Thread.java:748)
    
    "lettuce-nioEventLoop-4-1" #19 daemon prio=5 os_prio=0 tid=0x000055e97993c000 nid=0x52c7 runnable [0x00007f5b1c863000]
       java.lang.Thread.State: RUNNABLE
        at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
        at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:269)
        at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:93)
        at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)
        - locked <0x00000000c72cb848> (a io.netty.channel.nio.SelectedSelectionKeySet)
        - locked <0x00000000c7313218> (a java.util.Collections$UnmodifiableSet)
        - locked <0x00000000c72cb860> (a sun.nio.ch.EPollSelectorImpl)
        at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)
        at io.netty.channel.nio.SelectedSelectionKeySetSelector.select(SelectedSelectionKeySetSelector.java:62)
        at io.netty.channel.nio.NioEventLoop.select(NioEventLoop.java:737)
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:392)
        at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:884)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.lang.Thread.run(Thread.java:748)
    
    "Abandoned connection cleanup thread" #17 daemon prio=5 os_prio=0 tid=0x000055e9791d2000 nid=0x52c1 in Object.wait() [0x00007f5b1eb64000]
       java.lang.Thread.State: TIMED_WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
        - locked <0x00000000c66d5910> (a java.lang.ref.ReferenceQueue$Lock)
        at com.mysql.cj.jdbc.AbandonedConnectionCleanupThread.run(AbandonedConnectionCleanupThread.java:70)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)
    
    "NioBlockingSelector.BlockPoller-0" #12 daemon prio=5 os_prio=0 tid=0x00007f5b38648800 nid=0x52b0 runnable [0x00007f5b1f7e2000]
       java.lang.Thread.State: RUNNABLE
        at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
        at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:269)
        at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:93)
        at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)
        - locked <0x00000000c4de6dc8> (a sun.nio.ch.Util$3)
        - locked <0x00000000c4de6db8> (a java.util.Collections$UnmodifiableSet)
        - locked <0x00000000c4de6c90> (a sun.nio.ch.EPollSelectorImpl)
        at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)
        at org.apache.tomcat.util.net.NioBlockingSelector$BlockPoller.run(NioBlockingSelector.java:340)
    
    "GC Daemon" #11 daemon prio=2 os_prio=0 tid=0x00007f5b385b6000 nid=0x52af in Object.wait() [0x00007f5b2417d000]
       java.lang.Thread.State: TIMED_WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000c4ef7318> (a sun.misc.GC$LatencyLock)
        at sun.misc.GC$Daemon.run(GC.java:117)
        - locked <0x00000000c4ef7318> (a sun.misc.GC$LatencyLock)
    
    "AsyncFileHandlerWriter-1259475182" #10 daemon prio=5 os_prio=0 tid=0x00007f5b381fd800 nid=0x52ae waiting on condition [0x00007f5b24524000]
       java.lang.Thread.State: TIMED_WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000000c505faf0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
        at java.util.concurrent.LinkedBlockingDeque.pollFirst(LinkedBlockingDeque.java:522)
        at java.util.concurrent.LinkedBlockingDeque.poll(LinkedBlockingDeque.java:684)
        at org.apache.juli.AsyncFileHandler$LoggerThread.run(AsyncFileHandler.java:159)
    
    "Service Thread" #7 daemon prio=9 os_prio=0 tid=0x00007f5b38128800 nid=0x52ac runnable [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "C1 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007f5b3811d800 nid=0x52ab waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007f5b3811b800 nid=0x52aa waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007f5b3810c800 nid=0x52a9 runnable [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007f5b380e0800 nid=0x52a8 in Object.wait() [0x00007f5b24f87000]
       java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
        - locked <0x00000000c508a648> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)
    
    "Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007f5b380dc000 nid=0x52a7 in Object.wait() [0x00007f5b25088000]
       java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        at java.lang.Object.wait(Object.java:502)
        at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
        - locked <0x00000000c4ef7948> (a java.lang.ref.Reference$Lock)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
    
    "main" #1 prio=5 os_prio=0 tid=0x00007f5b3804c000 nid=0x52a3 runnable [0x00007f5b3ef39000]
       java.lang.Thread.State: RUNNABLE
        at java.net.PlainSocketImpl.socketAccept(Native Method)
        at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409)
        at java.net.ServerSocket.implAccept(ServerSocket.java:560)
        at java.net.ServerSocket.accept(ServerSocket.java:528)
        at org.apache.catalina.core.StandardServer.await(StandardServer.java:440)
        at org.apache.catalina.startup.Catalina.await(Catalina.java:776)
        at org.apache.catalina.startup.Catalina.start(Catalina.java:722)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:345)
        at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:476)
    
    "VM Thread" os_prio=0 tid=0x00007f5b380d2000 nid=0x52a6 runnable 
    
    "GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007f5b3805e800 nid=0x52a4 runnable 
    
    "GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007f5b38060800 nid=0x52a5 runnable 
    
    "VM Periodic Task Thread" os_prio=0 tid=0x00007f5b3813d800 nid=0x52ad waiting on condition 
    
    JNI global references: 1366

    2、线程的状态

    Thread.yield()方法的作用:暂停当前正在执行的线程,并执行其他线程。(可能没有效果) yield()让当前正在运行(Running)的线程回到可运行状态(Ready),以允许具有相同优先级的其他线程获得运行的机会。因此,使用yield()的目的是让具有相同优先级的线程之间能够适当的轮换执行。但是,实际中无法保证yield()达到让步的目的,因为,让步的线程可能被线程调度程序再次选中。

    在 Java中线程的状态一共被分成6种:

    1)、初始态( NEW)

    创建一个 Thread对象,但还未调用start()启动线程时,线程处于初始态。

    2)、运行态( RUNNABLE),在Java中,运行态包括 就绪态 和 运行态。

    (1)、就绪态(Ready)

    该状态下的线程已经获得执行所需的所有资源(如锁),只要 CPU分配执行权就能运行

    所有就绪态的线程存放在就绪队列中。

    (2)、运行态(Running)

    获得 CPU执行权,正在执行的线程。

    由于一个 CPU同一时刻只能执行一条线程,因此每个CPU每个时刻只有一条运行态的线程。

    3)、阻塞态( BLOCKED)

    当一条正在执行的线程请求某一资源失败时,就会进入阻塞态。

    而在 Java中,阻塞态专指请求锁失败时进入的状态。

    由一个阻塞队列存放所有阻塞态的线程。

    处于阻塞态的线程会不断请求资源,一旦请求成功,就会进入就绪队列,等待执行。

    4)、等待态( WAITING)

    当前线程中调用 wait、join、park函数时,当前线程就会进入等待态。

    也有一个等待队列存放所有等待态的线程。

    线程处于等待态表示它需要等待其他线程的指示才能继续运行。

    进入等待态的线程会释放 CPU执行权,并释放资源(如:锁)

    5)、计(超)时等待态( TIMED_WAITING)

    当运行中的线程调用 sleep(time)、wait、join、parkNanos、parkUntil时,就会进入该状态;

    它和等待态一样,并不是因为请求不到资源,而是主动进入,并且进入后需要其他线程唤醒;

    进入该状态后释放 CPU执行权 和 占有的资源。

    与等待态的区别:到了超时时间后自动进入阻塞队列,开始竞争锁。

    6)、终止态( TERMINATED)

    线程执行结束后的状态。

    3、死锁问题

    如果在生产环境发生了死锁,我们将看到的是部署的程序没有任何反应了,日志也没有任何输出,这个时候我们可以借助jstack进行分析,下面我们实战下查找死锁的原因。

    构造死锁

    编写代码,启动2个线程,Thread1拿到了obj1锁,准备去拿obj2锁时,obj2已经被Thread2锁定,所以发送了死锁。

      public class TestDeadLock {
        private static Object obj1 = new Object();
        private static Object obj2 = new Object();
        public static void main(String[] args) {
            new Thread(new Thread1()).start();
            new Thread(new Thread2()).start();
        }
        private static class Thread1 implements Runnable{
            @Override
            public void run() {
                synchronized (obj1){
                    System.out.println("Thread1 拿到了 obj1 的锁!");
                    try {
                        // 停顿2秒的意义在于,让Thread2线程拿到obj2的锁
                        Thread.sleep(2000); // 进入计时等待状态
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (obj2){ // 当线程1进入运行态时,发现锁2被线程2拿到了,故进入阻塞状态
                        System.out.println("Thread1 拿到了 obj2 的锁!");
                    }
                }
            }
        }
        private static class Thread2 implements Runnable{
            @Override
            public void run() {
                synchronized (obj2){
                    System.out.println("Thread2 拿到了 obj2 的锁!");
                    try {
                        // 停顿2秒的意义在于,让Thread1线程拿到obj1的锁
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (obj1){
                        System.out.println("Thread2 拿到了 obj1 的锁!");
                    }
                }
            }
        }
    }

    结果如下:

    Thread1 拿到了 obj1 的锁!
    Thread2 拿到了 obj2 的锁!

    发现没有任何反应了。

    下面在linux服务器中运行以上代码

    cd /test

    将代码拷贝到notepad++中,设置为utf8格式,然后保存为TestDeadLock.java文件,最后通过rz上传到/test目录

    编译

    javac TestDeadLock.java

    执行

    java TestDeadLock

    结果如下:

    [root@VM-0-10-centos test]# java TestDeadLock
    Thread1 拿到了 obj1 的锁!
    Thread2 拿到了 obj2 的锁!

    注意:此时进程并没有结束,程序也没有反应。这里发生了死锁,程序一直将等待下去

    打开另外一个会话窗口

    [root@VM-0-10-centos test]# jps
    21154 Bootstrap
    25094 TestDeadLock
    25430 Jps

    使用jstack命令

    [root@VM-0-10-centos test]# jstack 25094
    2022-01-07 22:35:37
    Full thread dump OpenJDK 64-Bit Server VM (25.312-b07 mixed mode):
    
    "Attach Listener" #11 daemon prio=9 os_prio=0 tid=0x00007fd7c0001000 nid=0x63d4 waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "DestroyJavaVM" #10 prio=5 os_prio=0 tid=0x00007fd7f804b800 nid=0x6207 waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "Thread-1" #9 prio=5 os_prio=0 tid=0x00007fd7f815c000 nid=0x6213 waiting for monitor entry [0x00007fd7db4cb000]
       java.lang.Thread.State: BLOCKED (on object monitor)
        at TestDeadLock$Thread2.run(TestDeadLock.java:37)
        - waiting to lock <0x00000000ec460c88> (a java.lang.Object)
        - locked <0x00000000ec460c98> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:748)
    
    "Thread-0" #8 prio=5 os_prio=0 tid=0x00007fd7f815a000 nid=0x6212 waiting for monitor entry [0x00007fd7db5cc000]
       java.lang.Thread.State: BLOCKED (on object monitor)
        at TestDeadLock$Thread1.run(TestDeadLock.java:20)
        - waiting to lock <0x00000000ec460c98> (a java.lang.Object)
        - locked <0x00000000ec460c88> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:748)
    
    "Service Thread" #7 daemon prio=9 os_prio=0 tid=0x00007fd7f8128000 nid=0x6210 runnable [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "C1 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007fd7f811d000 nid=0x620f waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007fd7f811a800 nid=0x620e waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007fd7f810c000 nid=0x620d runnable [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007fd7f80e0000 nid=0x620c in Object.wait() [0x00007fd7dbffe000]
       java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000ec408ee0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
        - locked <0x00000000ec408ee0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)
    
    "Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007fd7f80db800 nid=0x620b in Object.wait() [0x00007fd7e81f8000]
       java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000ec406c00> (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Object.java:502)
        at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
        - locked <0x00000000ec406c00> (a java.lang.ref.Reference$Lock)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
    
    "VM Thread" os_prio=0 tid=0x00007fd7f80d1800 nid=0x620a runnable 
    
    "GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007fd7f805e000 nid=0x6208 runnable 
    
    "GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007fd7f8060000 nid=0x6209 runnable 
    
    "VM Periodic Task Thread" os_prio=0 tid=0x00007fd7f813c800 nid=0x6211 waiting on condition 
    
    JNI global references: 5
    
    
    Found one Java-level deadlock:
    =============================
    "Thread-1":
      waiting to lock monitor 0x00007fd7d40062c8 (object 0x00000000ec460c88, a java.lang.Object),
      which is held by "Thread-0"
    "Thread-0":
      waiting to lock monitor 0x00007fd7d4004e28 (object 0x00000000ec460c98, a java.lang.Object),
      which is held by "Thread-1"
    
    Java stack information for the threads listed above:
    ===================================================
    "Thread-1":
        at TestDeadLock$Thread2.run(TestDeadLock.java:37)
        - waiting to lock <0x00000000ec460c88> (a java.lang.Object)
        - locked <0x00000000ec460c98> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:748)
    "Thread-0":
        at TestDeadLock$Thread1.run(TestDeadLock.java:20)
        - waiting to lock <0x00000000ec460c98> (a java.lang.Object)
        - locked <0x00000000ec460c88> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:748)

    Thread-0拿到了0x00000000ec460c88锁,等待去获取0x00000000ec460c98。Thread-1拿到了0x00000000ec460c98锁,等待去获取0x00000000ec460c88。

    由此可见,发生了死锁。

    七、VisualJVM工具的使用

    前面jstack和jmap都是通过命令行的方式查看,再借助其他工具辅助查看。VisualVM基本上涵盖了前面我们讲的所有命令,通过命令可以完成的动作,都可以在可视化工具中完成。

    VisualVM,能够监控线程,内存情况,查看方法的CPU时间和内存中的对 象,已被GC的对象,反向查看分配的堆栈(如100个String对象分别由哪几个对象分配出来的)。

    VisualVM使用简单,几乎0配置,功能还是比较丰富的,几乎囊括了其它JDK自带命令的所有功能。

    内存信息
    线程信息
    Dump 堆(本地进程)
    Dump 线程(本地进程)
    打开堆 Dump。堆Dump可以用jmap来生成。
    打开线程 Dump
    生成应用快照(包含内存信息、线程信息等等)
    性能分析。 CPU分析(各个方法调用时间,检查哪些方法耗时多),内存分析(各类对象占用的内存,检查哪些类占用内存多
    ……

    1、启动

    在jdk的安装目录的bin目录下,找到jvisualvm.exe,双击打开即可。

    当你在本机启动visual VM之后,就会自动查找本机的进程,

    第一个是IDEA进程,

    2、查看本地进程

    双击com.jawasoft.Application,

    3、查看CPU、内存、类、线程运行信息

    点击监视

    点击执行垃圾回收,并不是立刻执行垃圾回收,而是通知JVM进行垃圾回收,就相当于在代码中调用System.gc()。

    点击堆 dump

    点击类,

    在OQL控制台中可以通过对象查询语句查找对象。

    4、查看线程详情

    点击线程

    可以看到实时线程有31个,守护线程有29个。在左下角根据不同的颜色来表示线程的状态

    Java中有两种线程,一种是用户线程,另一种是守护线程。用户线程是指用户自定义创建的线程,主线程停止,用户线程不会停止。守护线程当进程不存在或主线程停止,守护线程也会被停止。

    点击线程 dump,就把线程dump下来了,其实就是执行的jstack命令。

    5、抽样器

    抽样器可以对CPU、内存在一段时间内进行抽样,以供分析。

    点击CPU,执行一段时间后点击停止,就可以看出在这一段时间之内CPU的执行情况。

    点击内存,让它执行一段时间,再点击停止,就可以看出在这一段时间之内,内存的使用情况,

    6、监控远程的jvm

    VisualJVM不仅是可以监控本地jvm进程,还可以监控远程服务器的jvm进程,需要借助于JMX技术实现。

    1)、什么是JMX?

    JMX(Java Management Extensions,即Java管理扩展)是一个为应用程序、设备、系统等植入管理功能的框架。JMX可以跨越一系列异构操作系统平台、系统体系结构和网络传输协议,灵活的开发无缝集成的系统、网络和服务管理应用。

    2)、监控远程的tomcat7

     想要监控远程的tomcat,就需要在远程的tomcat进行对JMX配置,方法如下:

    在tomcat的bin目录下,

    vim catalina.sh

    修改catalina.sh,添加如下的参数

    JAVA_OPTS="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9999 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false"

    这几个参数的意思是:

    ‐Dcom.sun.management.jmxremote :允许使用JMX远程管理

    ‐Dcom.sun.management.jmxremote.port=8080 :JMX远程连接端口,我们就可以通过这个端口查看远程服务器。

    ‐Dcom.sun.management.jmxremote.authenticate=false :不进行身份认证,任何用户都可以连接,实际开发中这样是不安全的。

    ‐Dcom.sun.management.jmxremote.ssl=false :不使用ssl

    注意:要在云服务器中开通该端口,而且防火墙也要开发该端口。

    JAVA_OPTS命令在tomcat启动的时候使用,会把参数添加到启动的jvm中。

    保存退出,重启tomcat。

    如果是tomcat8

    编辑tomcat/bin/catalina.sh

    3)、使用VisualJVM连接远程tomcat

    单击远程,添加远程主机,输入主机IP,点击确定

    在一个主机下可能会有很多的 jvm需要监控,所以接下来要在该主机上添加需要监控的jvm:

    单击主机,右键,选择添加JMX连接,

     点击确定

     

    连接成功。使用方法和前面就一样了,就可以和监控本地 jvm进程一样,监控远程的tomcat进程。

  • 相关阅读:
    01月04日假期学习
    个人加分项
    12月16日总结
    12月15日总结
    12月13日总结
    01月01日假期学习
    01月02日假期学习
    12月14日总结
    12月17日总结
    01月05日假期学习
  • 原文地址:https://www.cnblogs.com/zwh0910/p/15767086.html
Copyright © 2020-2023  润新知