Jvm调优-高CPU和高内存问题分析

530 阅读6分钟

参考链接:www.cnblogs.com/Dhouse/p/78…

java启动项:
    Xms 是指设定程序启动时占用内存大小
    Xmx 是指设定程序运行期间最大可占用的内存大小(如果程序运行需要占用更多的内存,超出了这个设置值,就会抛出OutOfMemory异常。)
    Xmn 是指年轻代大小
    Xss 是指设定每个线程的堆栈大小。这个就要依据你的程序,看一个线程大约需要占用多少内存,可能会有多少线程同时运行等。
    
    # 启动项增加GC配置:
    -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log

1、高CPU占用

    ps -mp pid -o THREAD,tid,time
    printf "%x\n" tid
    # 最后打印线程的堆栈信息:
    # jstack:Java提供的命令。可以查看某个进程的当前线程栈运行情况。
    # 根据这个命令的输出可以定位某个进程的所有线程的当前运行状态、运行代码,以及是否死锁等等。
    jstack pid |grep tid -A 30

2、高内存占用


    一般情况:
    1、java.lang.OutOfMemoryError: PermGen space
    2、java.lang.OutOfMemoryError: Java heap space #java1.8后已经讲永久代去除,只有元空间了,因此不会出现这个问题
    
    在Java虚拟机中,内存分为三个代:新生代(New)、老生代(Old)、永久代(Perm)
    (1)新生代New:新建的对象都存放这里
    (2)老生代Old:存放从新生代New中迁移过来的生命周期较久的对象。新生代New和老生代Old共同组成了堆内存。
    (3)永久代Perm:是非堆内存的组成部分。主要存放加载的Class类级对象如class本身,method,field等等。
    
    
    一、java.lang.OutOfMemoryError: Java heap spac #说明Java虚拟机的堆内存不够,Java虚拟机的堆内存设置不够,可以通过参数-Xms、-Xmx来调整。
    
    二、java.lang.OutOfMemoryError: PermGen space #说明是Java虚拟机对永久代Perm内存设置不够。一般出现这种情况,都是程序启动需要加载大量的第三方jar包。
    
    # 内存占用高的实例:
    ps -mp pid -o THREAD,tid,time,rss,size,%mem
    
    #jmap查看内存情况,什么class有多少
    #可以查看当前Java进程创建的活跃对象数目和占用内存大小
    jmap -histo:live pid  
    # YGC就是年轻代GC, FGC就是FullGC, T就是时间
      S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT   
      0.00   0.00   1.12   1.15  98.13  96.38      9    0.049     2    0.056    0.105
      0.00   0.00   1.12   1.15  98.13  96.38      9    0.049     2    0.056    0.105
      0.00   0.00   1.12   1.15  98.13  96.38      9    0.049     2    0.056    0.105
      0.00   0.00   1.12   1.15  98.13  96.38      9    0.049     2    0.056    0.105
      0.00   0.00   1.12   1.15  98.13  96.38      9    0.049     2    0.056    0.105
    
    #则可以将当前Java进程的内存占用情况导出来,方便用专门的内存分析工具(例如:MAT)来分析。
    jmap -dump:live,format=b,file=jamp.log pid #会导致full gc, 因此执行该命令需谨慎
    
    #jstat动态查看年轻代,老年代的gc情况
    jstat -gcutil 17604 #会导致full gc, 因此执行该命令需谨慎
    #下面就是类的名,有多少实例,占用了多少内存
     num     #instances         #bytes  class name
    ----------------------------------------------
       1:         10545         805704  [C
       2:           567         353336  [J
       3:          2393         271800  java.lang.Class
       4:           470         254944  [B
       5:         10506         252144  java.lang.String
       6:          2521         128512  [I
       7:          1855          99608  [Ljava.lang.Object;
       8:          2707          86624  java.util.concurrent.ConcurrentHashMap$Node
       9:          1989          63648  java.util.HashMap$Node
      10:           613          53944  java.lang.reflect.Method
      11:           751          42056  sun.util.calendar.ZoneInfo
      12:           355          36248  [Ljava.util.HashMap$Node;
      13:          1777          28432  java.lang.Object
      14:           666          26640  java.util.LinkedHashMap$Entry
      15:            51          26296  [Ljava.util.concurrent.ConcurrentHashMap$Node;
      16:           423          16920  java.lang.ref.SoftReference
      17:           483          16512  [Ljava.lang.String;

JVM内存如何进行和常用分析命令汇总:

(1)如何分配内存

    # 根据gc的情况,看年轻代和老年代的内存设置
    1、直接使用默认的启动,然后查看gc情况
    2、根据开发本地电脑的配置来设置
    3、后期根据gc情况调整具体的大小

(2)常用分析命令

[root@stage-lab-api ~]# jstat -gcutil 17604 1000
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT   
  0.00   0.00   1.12   1.15  98.13  96.38      9    0.049     2    0.056    0.105
  0.00   0.00   1.12   1.15  98.13  96.38      9    0.049     2    0.056    0.105
  0.00   0.00   1.12   1.15  98.13  96.38      9    0.049     2    0.056    0.105
  0.00   0.00   1.12   1.15  98.13  96.38      9    0.049     2    0.056    0.105
  0.00   0.00   1.12   1.15  98.13  96.38      9    0.049     2    0.056    0.105
[root@stage-lab-api ~]# jmap -histo:live 17604 | head -n 20 #(这个命令会导致gc)
 num     #instances         #bytes  class name
----------------------------------------------
   1:         10545         805704  [C
   2:           567         353336  [J
   3:          2393         271800  java.lang.Class
   4:           470         254944  [B
   5:         10506         252144  java.lang.String
   6:          2521         128512  [I
   7:          1855          99608  [Ljava.lang.Object;
   8:          2707          86624  java.util.concurrent.ConcurrentHashMap$Node
   9:          1989          63648  java.util.HashMap$Node
  10:           613          53944  java.lang.reflect.Method
  11:           751          42056  sun.util.calendar.ZoneInfo
  12:           355          36248  [Ljava.util.HashMap$Node;
  13:          1777          28432  java.lang.Object
  14:           666          26640  java.util.LinkedHashMap$Entry
  15:            51          26296  [Ljava.util.concurrent.ConcurrentHashMap$Node;
  16:           423          16920  java.lang.ref.SoftReference
  17:           483          16512  [Ljava.lang.String;
[root@pd-disp-gateway ~]# jmap -heap 10513
Attaching to process ID 10513, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.192-b12

using thread-local object allocation.
Parallel GC with 4 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 2147483648 (2048.0MB)
   NewSize                  = 268435456 (256.0MB)
   MaxNewSize               = 268435456 (256.0MB)
   OldSize                  = 1879048192 (1792.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 = 263192576 (251.0MB)
   used     = 9582776 (9.138847351074219MB)
   free     = 253609800 (241.86115264892578MB)
   3.640975040268613% used
From Space:
   capacity = 2621440 (2.5MB)
   used     = 0 (0.0MB)
   free     = 2621440 (2.5MB)
   0.0% used
To Space:
   capacity = 2621440 (2.5MB)
   used     = 0 (0.0MB)
   free     = 2621440 (2.5MB)
   0.0% used
PS Old Generation
   capacity = 1879048192 (1792.0MB)
   used     = 27599024 (26.320480346679688MB)
   free     = 1851449168 (1765.6795196533203MB)
   1.4687768050602503% used

22532 interned Strings occupying 2936192 bytes.

(3)OOM分析

最常见的OOM情况有以下三种:

java.lang.OutOfMemoryError: Java heap space ------>
java堆内存溢出,此种情况最常见,一般由于内存泄露或者堆的大小设置不当引起。对于内存泄露,需要通过内存监控软件查找程序中的泄露代码,而堆大小可以通过虚拟机参数-Xms,-Xmx等修改。

# java8中,永久代被换成了元空间,不会再出现以下情况,当然也可以再java8中限制。
java.lang.OutOfMemoryError: PermGen space ------>
java永久代溢出,即方法区溢出了,一般出现于大量Class或者jsp页面,或者采用cglib等反射机制的情况,因为上述情况会产生大量的Class信息存储于方法区。此种情况可以通过更改方法区的大小来解决,使用类似-XX:PermSize=64m -XX:MaxPermSize=256m的形式修改。另外,过多的常量尤其是字符串也会导致方法区溢出。

java.lang.StackOverflowError ------> 
不会抛OOM error,但也是比较常见的Java内存溢出。JAVA虚拟机栈溢出,一般是由于程序中存在死循环或者深度递归调用造成的,栈大小设置太小也会出现此种溢出。可以通过虚拟机参数-Xss来设置栈的大小。

(4)GC OOM发生条件:

对象一般在新生代的Eden区分配空间,大对象(超过阈值)和(大)数组会直接在老年代分配;

JVM默认老年代和年轻代空间比例3:1,新生代默认Eden和survivor区比例8:1(两个survivor,所以是8:1:1);

如果新生代空间不够(触发阈值),会触发minor GC,回收年轻代的垃圾,使用复制算法,超过阈值年龄的存活对象会进入老年代;

如果分配空间时老年代空间不够(达到阈值),会触发major GC(就是FUll GC),算法根据垃圾收集器的不同而有所差别,Full GC 之后还是不够,就会抛出OOM错误。

Java 8没有永久代,MetaSpace是堆外内存

元空间与永久代最大的区别在于,元空间不在虚拟机中,使用本地内存。通过配置如下参数可以更改元空间的大小。
-XX:MetaspaceSize:初始空间的大小。达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。
-XX:MaxMetaspaceSize,最大空间,默认是没有限制的。
永久代的回收会随着full gc进行移动,消耗性能。每种类型的垃圾回收都需要特殊处理元数据。将元数据剥离出来,简化了垃圾收集,提高了效率。