zoukankan      html  css  js  c++  java
  • (案例8)java性能定位

    内存溢出:

    栈溢出(java.lang.StackOverflowError):

     1)java栈空间存储的是什么。java栈空间是线程私有的,是java方法执行的内存模型。每个方法执行时都会在java栈空间产生一个栈帧,存放方法的变量表,返回值等信息,方法的执行到结束就是一个栈帧入栈到出栈的过程。

    2)栈溢出的原因一般是循环调用方法导致栈帧不断增多,栈深度不断增加,最终没有内存可以分配,出现StackOverflowError

    堆溢出( java.lang.OutOfMemoryError:Java heap space):

    1)java堆是线程共有的区域,主要用来存放对象实例,几乎所有的java对象都在这里分配内存,也是JVM内存管理最大的区域。java堆内存分年轻代和年老代,堆内存溢出一般是年老代溢出。当程序不断地创建大量对象实例并且没有被GC回收时,就容易产生内存溢出

    2)java堆内存分年轻代和年老代,堆内存溢出一般是年老代溢出。当程序不断地创建大量对象实例并且没有被GC回收时,就容易产生内存溢出

    3)堆内存溢出很可能伴随内存泄漏,应首先排查可能泄露的对象,再通过工具检查GC roots引用链,从而发现泄露对象是由于何种引用关系使得GC无法回收他们;若不存在内存泄漏,换句话说就是内存中的对象还都需要继续存活,则可通过修改虚拟机的堆参数将堆内存增大

    持久代溢出(Java.Lang.OutOfMemoryError: PermGen Space):

    1)永久代也是java堆内存的一部分,主要用来存放Class的相关信息,如类名,访问修饰符等等。一般永久代溢出的原因是动态加载大量的Class并且没有及时被GC回收。只能通过调整永久代内存参数的方式解决

    一、线程死锁

    jstack主要用来查看某个Java进程内的线程堆栈信息。语法格式如下:

    jstack [option] pid
    jstack [option] executablecore
    jstack [option] [server-id@]remote-hostname-or-ip
    

    命令行参数如下:

    -l long listings,会打印出额外的锁信息,在发生死锁时可以用jstack -l pid来观察锁持有情况
    -m mixed mode,不仅会输出Java堆栈信息,还会输出C/C++堆栈信息(比如Native方法)
    

    jstack可以定位到线程堆栈,根据堆栈信息我们可以定位到具体代码,所以它在JVM性能调优中使用得非常多。找出某个Java进程中最耗费CPU的Java线程并定位堆栈信息,用到的命令有ps、top、printf、jstack、grep。操作如下:
    1.查询Java进程ID,如下图:

    1.查询Java进程ID,如下图:

    2.通过进程ID查询对CPU消耗最大的线程,可以使用ps -Lfp pid或者ps -mp pid -o THREAD,tid,time或者top -Hp pid,我这里用第三个,如下图:

    将线程ID转成16进制用于查询,使用如下命令即可实现:

    printf "%x
    " pid
    

    3.使用jstack输出进程的堆栈信息,如下图:

    jstack 2838 | grep 12c8 -A30
    • 2838:java 进程
    • 12c8:线程十六进制
    • -A30:打印 30 行

    线程状态:

    死锁,Deadlock(重点关注)
    执行中,Runnable
    等待资源,Waiting on condition(重点关注)
    等待获取监视器,Waiting on monitor entry(重点关注)
    暂停,Suspended
    对象等待中,Object.wait() 或 TIMED_WAITING
    阻塞,Blocked(重点关注)
    停止,Parked

    二、堆内存(堆溢出)

    jmap(Memory Map)用来查看堆内存使用状况,一般结合jhat(Java Heap Analysis Tool)使用。语法格式如下:

    jmap [option] pid
    jmap [option] executablecore
    jmap [option] [server-id@]remote-hostname-or-ip
    

    1.没有参数打印进程的类加载器和类加载器加载的持久代对象信息,输出:类加载器名称、对象是否存活(不可靠)、对象地址、父类加载器、已加载的类大小等信息,如下图:

    jmap pid
    


    2.查看进程堆内存使用情况,包括使用的GC算法、堆配置参数和各代中堆内存使用情况。比如下面的例子:

    jmap -heap pid
    


    3.使用jmap -histo[:live] pid查看堆内存中的对象数目、大小统计直方图,如果带上live则只统计活对象并且会强制执行一次GC,如下:

    jmap -histo pid
    jmap -histo:live pid
    



    class name是对象类型,说明如下:

    B  byte
    C  char
    D  double
    F  float
    I  int
    J  long
    Z  boolean
    [  数组,如[I表示int[]
    [L+类名 其他对象
    

    还有一个很常用的情况是:用jmap把进程内存使用情况dump到文件中,再用jhat分析查看。jmap进行dump命令格式如下:

    jmap -dump:format=b,file=dumpFileName pid (文件的后缀建议采用".hprof")
    


    dump出来的文件可以用MAT、VisualVM等工具查看,这里用jhat查看:

    jhat -port xxxx dumpFileName
    


    然后就可以在浏览器中输入主机地址:port查看了:

    三、GC(年轻和老年)

    jstat(JVM统计监测工具),监控的内容有:类装载、内存、垃圾收集、jit编译的信息。语法格式如下:

    jstat [ generalOption | outputOptions vmid [interval[s|ms] [count]] ]

    说明:Options,选项,我们一般使用 -gcutil 查看gc情况 vmid,VM的进程号,即当前运行的java进程号 interval,间隔时间,单位为秒或者毫秒 count,打印次数,如果缺省则打印无数次

     
    通常运行命令如下:
    jstat -gc 12538 5000
    即会每5秒一次显示进程号为12538的java进成的GC情况,
    显示内容如下图:
     
    jstat -gcutil 28363 1s 
    jstat -gccause pid 1 每格1毫秒输出结果
    jstat -gccause pid 2000 每格2秒输出结果
     
    jstat -gc 21711 250 4
    
    注:vmid是虚拟机ID,在Linux/Unix系统上一般就是进程ID。interval是采样时间间隔。count是采样数目。比如下面输出的是GC信息,采样时间间隔为250ms,采样数为4
    要明白上面各列的意义,先看JVM堆内存布局:


    可以看出:

    堆内存 = 年轻代 + 年老代 + 永久代
    年轻代 = Eden区 + 两个Survivor区(From和To)

    字段含义:

    S0C、S1C、S0U、S1U:Survivor 0/1区容量(Capacity)和使用量(Used)
    EC、EU:Eden区容量和使用量
    OC、OU:年老代容量和使用量
    PC、PU:永久代容量和使用量
    YGC、YGT:年轻代GC次数和GC耗时
    FGC、FGCT:Full GC次数和Full GC耗时
    GCT:GC总耗时

    通过gc判断是否有gc溢出

    可以通过每次GC后 各区的占用率发生的变化来判断GC成果:
    
    1、如果FULL GC频繁说明内存已经存在溢出可能或已经溢出,应用处理能力将会下降甚至停摆,因为大量时间在GC上面
    
    2、如果YGC频繁,s0,s1,eden区使用率又没有明显下降,也需要注意,他们中的数据在增长到一定程度时,可能会进入old区,需要观察full gc情况
    
    3、如果每次YGC后,但是s0,s1,eden区内存使用下降明显,一般不会有问题,说明jvm中存在了占比较重的短生命周期对象,能及时回收掉释放给其它请求或任务使用,只要保持获取与释放平衡就不会有太大问题
    
    ps:当创建对象时前区剩余空间不够的时候就会触发YGC,如果回收后还不够则会转移到后一区
    显示内容说明如下(部分结果是通过其他其他参数显示的,暂不说明):
    S0C:年轻代中第一个survivor(幸存区)的容量 (字节)         
    S1C:年轻代中第二个survivor(幸存区)的容量 (字节)         
    S0U:年轻代中第一个survivor(幸存区)目前已使用空间 (字节)         
     
    S1U:年轻代中第二个survivor(幸存区)目前已使用空间 (字节)         
    EC:年轻代中Eden(伊甸园)的容量 (字节)         
    EU:年轻代中Eden(伊甸园)目前已使用空间 (字节)         
    OC:Old代的容量 (字节)         
    OU:Old代目前已使用空间 (字节)         
    PC:Perm(持久代)的容量 (字节)         
    PU:Perm(持久代)目前已使用空间 (字节)         
    YGC:从应用程序启动到采样时年轻代中gc次数         
    YGCT:从应用程序启动到采样时年轻代中gc所用时间(s)         
    FGC:从应用程序启动到采样时old代(全gc)gc次数         
    FGCT:从应用程序启动到采样时old代(全gc)gc所用时间(s)         
    GCT:从应用程序启动到采样时gc用的总时间(s)         
    NGCMN:年轻代(young)中初始化(最小)的大小 (字节)         
    NGCMX:年轻代(young)的最大容量 (字节)         
    NGC:年轻代(young)中当前的容量 (字节)         
    OGCMN:old代中初始化(最小)的大小 (字节)         
    OGCMX:old代的最大容量 (字节)         
    OGC:old代当前新生成的容量 (字节)         
    PGCMN:perm代中初始化(最小)的大小 (字节)         
    PGCMX:perm代的最大容量 (字节)           
    PGC:perm代当前新生成的容量 (字节)         
    S0:年轻代中第一个survivor(幸存区)已使用的占当前容量百分比         
    S1:年轻代中第二个survivor(幸存区)已使用的占当前容量百分比         
    E:年轻代中Eden(伊甸园)已使用的占当前容量百分比         
    O:old代已使用的占当前容量百分比         
    P:perm代已使用的占当前容量百分比         
    S0CMX:年轻代中第一个survivor(幸存区)的最大容量 (字节)         
    S1CMX :年轻代中第二个survivor(幸存区)的最大容量 (字节)         
    ECMX:年轻代中Eden(伊甸园)的最大容量 (字节)         
    DSS:当前需要survivor(幸存区)的容量 (字节)(Eden区已满)         
    TT: 持有次数限制         
    MTT : 最大持有次数限制
  • 相关阅读:
    【AtCoder】AtCoder Grand Contest 014 解题报告
    【CF603E】Pastoral Oddities(CDQ分治)
    【洛谷4654】[CEOI2017] Mousetrap(DP+二分)
    【洛谷4800】[CEOI2015 Day2] 核能国度(差分细节题)
    【CF626G】Raffles(贪心)
    【CF578E】Walking!(贪心)
    【AtCoder】AtCoder Grand Contest 015 解题报告
    【CF582E】Boolean Function(动态规划+FWT)
    【CF576E】Painting Edges(线段树分治+并查集)
    【CF576D】Flights for Regular Customers(矩乘套路题)
  • 原文地址:https://www.cnblogs.com/uestc2007/p/14756725.html
Copyright © 2011-2022 走看看