先来看一下java 虚拟机的各个含义html
-Xms 堆的初始值 -Xmx 堆的最大值 -Xmn 堆中新生代的值 -XX:MetaspaceSize 元数据区值 -XX:MaxMetaspaceSize 元数据区最大值 -Xss 线程空间大小 -XX:SurvivorRatio 新生代中 伊甸区与s0 和s1的比例 -XX:NewRatio 老年代与新生代的比例
上述内容有以下注意:java
堆包括 新生代和老年代。 新生代 包括 伊甸区 、s0、s1 。 元数据区占用堆外内存,直接使用物理内存,若是不设置最大值,则会无限上涨。 -xss大小为线程空间,使用的也是 堆外内存,直接占用物理内存,在物理内存必定量的状况下:该值越大,则整个系统可以使用的线程越少,但栈的递归深度会增长。
定位问题:linux
场景: 一个桌面的java swing程序,是一个工具大集合,再点击了其中的几个工具后,发现整个工具开始卡顿。 先看一下拉起时的bat脚本中的java参数:安全
set JVM_OPTS=-Xms32m -Xmx64m -Xmn16m -XX:MetaspaceSize=32m -XX:MaxMetaspaceSize=32m网络
因为是一个桌面的程序,因此,没有给太多的内存,根据参数,能够得出各个区域的大小为:jvm
最大堆时为 64Mxss
老年代为 48M工具
新生代为 16M性能
元数据区 为32Mspa
对于性能问题,有两个定位思路: 一个是没有内存卡,第二个是cpu被占用卡,三是看网络卡不卡(和网络相关)
好,那咱们一个一个看,首先咱们先看cpu被占用卡顿
从window 任务管理器 查看到咱们启动java程序,看一下 程序 占用的cpu,若是很高,则说明是线程出了问题。 那咱们须要将java进程找到究竟是哪一个线程 出的问题,此时须要借助工具ProcessExplorer 来查看线程
打开工具,找到对应的进程,右击 属性,在thread 页面,能够看到 线程的id。
此时,咱们须要先借助jstack 进程id 命令,打印出java 进程中素有的线程,
下一步,咱们要讲以前看到的cpu使用很高的线程id 变成16进制,而后在 jstack 线程 信息中,寻找nid 为该值的线程,根据线程的信息,肯定究竟是哪一个类除了问题。
若是是linux系统的话,这能够经过top 产看进程 top -Hp 进程id 产看线程id,后面处理就和window的同样了。
程序卡住不动时,咱们先用 jstat -gc 进程id 来获取 当前的 内存使用状况以下
C:\Windows\system32>jstat -gc 4848 1000 20 S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 512.0 512.0 0.0 0.0 15360.0 2.0 49152.0 10068.4 32768.0 32079.0 3712.0 3601.6 3150 4.263 2943 110.611 114.874 512.0 512.0 0.0 0.0 15360.0 2.0 49152.0 10068.4 32768.0 32079.0 3712.0 3601.6 3150 4.263 2943 110.611 114.874 512.0 512.0 0.0 0.0 15360.0 2.0 49152.0 10068.4 32768.0 32079.0 3712.0 3601.6 3150 4.263 2943 110.611 114.874 512.0 512.0 0.0 0.0 15360.0 2.0 49152.0 10068.4 32768.0 32079.0 3712.0 3601.6 3150 4.263 2943 110.611 114.874
其中 4848 是进程id,1000为1s采集一次 20表明采集20次
须要关注的是,这个地方列出的 单位是KB 其中
s0c表明新生代的s0 区分配0.5M的内存 s1c表明新生代的s1 区分配0.5M的内存 s0u表明新生代的s0 区使用了0 内存 s1u表明新生代的s1 区使用了0 内存 EC表明新生代伊甸区为15M EU表明新生代伊甸区使用了2KB OC OU 表明老年代 分配空间和使用状况,当前分配了48M,用了10M CCSC:压缩类空间大小 CCSU:压缩类空间使用大小 YGC:年轻代垃圾回收次数 YGCT:年轻代垃圾回收消耗时间 FGC:老年代垃圾回收次数 FGCT:老年代垃圾回收消耗时间 GCT:垃圾回收消耗总时间
重点关注 的是 MC和MU 这两列的值,发如今程序卡顿的时候,这个值很是接近咱们对jvm 设置参数时候的最大值32M
也就是说:元数据区 的空间不够了!
那么这个元数据区 是干啥的? 其做用和jdk7及其以前的版本中的方法区同样,是用来存储 类信息,固然jdk8 以后 叫元数据区了,和jdk7有区别。
竟然是加载的类太多了?
那如何看到jvm加载了那些类呢?
在jvm的参数上面加上 -verbose:class 能够看到加载了那些类 推荐 将 结果 保存到文件,而后分析
可是你会发现 有些类名 被压缩了,根本看不出来,此时,你须要再加上一个参数
-Dcom.sun.xml.bind.v2.bytecode.ClassTailor.noOptimize= true
此命令也能够表示直接关闭bytecode反射机制
加上jvm参数后,能够继续运行程序,看一下究竟是加载了那些类,致使的这个问题。
后面定位分析,就须要你结合自身的代码,肯定究竟是哪一个类,加载过多,因为公司安全,我没法上代码了。
最后:这里再补充一下,在发生性能问题的时候,应该说100%的问题是cpu使用率问题形成的,而堆内存不够或者是元数据内存不够,只是 java卡顿在内存上的黑盒表现,在卡顿时,经过监控java 进程中线程的使用率,你会发现,cpu 很高的每每是 GC线程,这时,你要关注的本质上,仍是内存不够了。
最后,附上两位大神的定位记录,很是棒: