首先我们要知道jvm的调优,主要是对那些部分的优化。通过jvm内存模型我们可以,首先是分析遇到的问题,然后通过一些工具或者手段找到问题所在,然后通过一定的措施解决问题,下面我们也将按着这个思路来给出具体的操作。
问题分析
这个主要是根据我们在运行程序时出现的问题:内存溢出,栈溢出,或者请求停顿。
解决方案
堆内存溢出的话我们首先看jvm的配置参数:
- java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0
-Xmx3550m:设置JVM最大可用内存为3550M。
-Xms3550m:设置JVM促使内存为3550m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
-Xmn2g:设置年轻代大小为2G。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
-Xss128k:设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。
-XX:NewRatio=4:设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5。但是如果设置了年轻代的大小和最大最小对内存,这个值就没有必要设置了。
-XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6.一般默认比值为2/8
-XX:MaxPermSize=64m:设置永久代大小为64m。一般设置为物理内存的1/64。(这个可以看着做事方法区的大小,在HotSpot虚拟机中方法去和永久带是一个概念)
-XX:MaxTenuringThreshold=0:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。
栈溢出
主要有栈溢出两种情景:
- 线程请求的栈的深度大于虚拟机所允许的最大深度,将抛出*Error
栈的深度和设置的-xss的大小以及和局部变量的数量以及大小有关,-xss越小,局部变量个数越多长度越长,深度就越小。栈帧存放着局部变量,方法返回值,这些值越多,或者值越大,那么战阵就越大,占用的内存就越大,栈的深度就越小。
- 虚拟机在扩展栈时无法申请到足够的空间,将抛出OutOfMemoryError
栈的大小(虚拟机栈和本地方法栈瓜分的大小)=系统限制的内存的大小-最大堆内存的大小(Xmx)-最大方法区内存(MaxPermSize)-程序计数器(很小)-虚拟机本身耗费的内存(很小)
方法区溢出
常量池内存溢出:运行时常量池也属于方法区
其他方法区内存溢出:主要是存放class相关信息的地方。主要是大量使用cglib技术,增强类越多,就需要越大的方法区内存。
使用到的工具
- jstack:java堆栈跟踪工具
此工具是查看某个进程下的当前时刻线程的状态信息的,生成虚拟机当前时刻的线程快照,线程快照就是当前虚拟机中每一条线程正在执行的方法堆栈的集合,主要目的是定位线程长时间停顿的原因。
命令格式:
jstack [ option ] pid
基本参数:
-F 当'jstack [-l] pid'没有相应的时候强制打印栈信息
-l 长列表. 打印关于锁的附加信息,例如属于java.util.concurrent的ownable synchronizers列表.
-m 打印java和native c/c++框架的所有栈信息. -h | -help打印帮助信息
pid 需要被打印配置信息的java进程id,可以用jps工具查询
重点关注的线程状态:
死锁, Deadlock(重点关注)
执行中,Runnable
等待资源, Waiting on condition(重点关注)
等待获取监视器, Waiting on monitor entry(重点关注)
暂停,Suspended
对象等待中,Object.wait() 或 TIMED_WAITING
阻塞, Blocked(重点关注)
停止,Parked
线程状态解析
Deadlock:死锁线程,一般指多个线程调用间,进入相互资源占用,导致一直等待无法释放的情况。
Runnable:一般指该线程正在执行状态中,该线程占用了资源,正在处理某个请求,有可能正在传递SQL到数据库执行,有可能在对某个文件操作,有可能进行数据类型等转换。
Waiting on condition:
该状态出现在线程等待某个条件的发生。具体是什么原因,可以结合 stacktrace来分析。最常见的情况是线程在等待网络的读写,
比如当网络数据没有准备好读时,线程处于这种等待状态,而一旦有数据准备好读之后,线程会重新激活,读取并处理数据。在 Java引入 NewIO之前,对于每个网络连接,
都有一个对应的线程来处理网络的读写操作,即使没有可读写的数据,线程仍然阻塞在读写操作上,这样有可能造成资源浪费,而且给操作系统的线程调度也带来压力。
在 NewIO里采用了新的机制,编写的服务器程序的性能和可扩展性都得到提高。
如果发现有大量的线程都在处在 Wait on condition,从线程 stack看, 正等待网络读写,这可能是一个网络瓶颈的征兆。因为网络阻塞导致线程无法执行。
一种情况是网络非常忙,几 乎消耗了所有的带宽,仍然有大量数据等待网络读 写;另一种情况也可能是网络空闲,但由于路由等问题,导致包无法正常的到达。
所以要结合系统的一些性能观察工具来综合分析,比如 netstat统计单位时间的发送包的数目,如果很明显超过了所在网络带宽的限制 ; 观察 cpu的利用率,如果系统态的 CPU时间,
相对于用户态的 CPU时间比例较高;如果程序运行在 Solaris 10平台上,可以用 dtrace工具看系统调用的情况,如果观察到 read/write的系统调用的次数或者运行时间遥遥领先;
这些都指向由于网络带宽所限导致的网络瓶颈。另外一种出现 Wait on condition的常见情况是该线程在 sleep,等待 sleep的时间到了时候,将被唤醒。
blocked:线程阻塞,是指当前线程执行过程中,所需要的资源长时间等待却一直未能获取到,被容器的线程管理器标识为阻塞状态,可以理解为等待资源超时的线程。
Waiting for monitor entry 和 in Object.wait():Monitor是 Java中用以实现线程之间的互斥与协作的主要手段,它可以看成是对象或者 Class的锁。每一个对象都有,也仅有一个 monitor。
jstack报告分析
- jstat:虚拟机统计信息监视工具
主要显示虚拟机进程中的类装载,内存,垃圾收集,jit编译等运行时的数据,是定位云定期虚拟机问题的首选工具
命令格式:
jstat -<option> [-t] [-h<lines>] <vmid> [<interva[s|ms]> [<count>]]
参数解释:
Options — 选项,我们一般使用 -gcutil 查看gc情况
vmid — VM的进程号,即当前运行的java进程号
interval[s|ms] —— 间隔时间,单位为秒或者毫秒,默认为ms。必须是正整型。
count — 打印次数,如果缺省则打印无数次
选项参数
jstat工具特别强大,有众多的可选项,详细查看堆内各个部分的使用量,以及加载类的数量。使用时,需加上查看进程的进程id,和所选参数。以下详细介绍各个参数的意义。
jstat -class pid:显示加载class的数量,及所占空间等信息。
jstat -compiler pid:显示VM实时编译的数量等信息。
jstat -gc pid:可以显示gc的信息,查看gc的次数,及时间。其中最后五项,分别是young gc的次数,young gc的时间,full gc的次数,full gc的时间,gc的总时间。
jstat -gccapacity:可以显示,VM内存中三代(young,old,perm)对象的使用和占用大小,如:PGCMN显示的是最小perm的内存使用量,PGCMX显示的是perm的内存最大使用量,PGC是当前新生成的perm内存占用量,PC是但前perm内存占用量。其他的可以根据这个类推, OC是old内纯的占用量。
jstat -gcnew pid:new对象的信息。
jstat -gcnewcapacity pid:new对象的信息及其占用量。
jstat -gcold pid:old对象的信息。
jstat -gcoldcapacity pid:old对象的信息及其占用量。
jstat -gcpermcapacity pid: perm对象的信息及其占用量。
jstat -gcutil pid:统计gc信息统计。
jstat -printcompilation pid:当前VM执行的信息。
- jmap:java内存映像工具
主要是用于生成堆转存快照。
命令行格式:
jmap [option] LVMID(LVMID指的是进程ID)
option参数:
- dump : 生成堆转储快照,可以再windows下使用
- finalizerinfo : 显示在F-Queue队列等待Finalizer线程执行finalizer方法的对象,不可以再windows下使用
- heap : 显示Java堆详细信息,不可以再windows下使用
- histo : 显示堆中对象的统计信息,包括类,实例数量,合计荣力量。可以再windows下使用
- permstat : to print permanent generation statistics,不可以再windows下使用
- F : 当-dump没有响应时,强制生成dump快照,不可以再windows下使用
- jinfo
- jps
- jhat
可视化工具
- jconsole
- visualVM