(案例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 : 最大持有次数限制
原文地址:https://www.cnblogs.com/uestc2007/p/14756725.html