jvm分析&调优

                                               jvm分析&调优

在平时的java开发工作中,特别是将应用部署到服务器之后,经常会出现各种各样的问题,例如内存泄漏、死锁、CPU飙高等。下面我们就来学习掌握一些工具来分析到底是哪里出了问题,做到及时定位问题、有效解决问题。

cpu占用过高排查思路

  1. top 查看占用cpu的进程 pid
  2. top -Hp pid 查看进程中占用cpu过高的线程id  tid
  3. printf '%x/n' tid 转化为十六进制
  4. jstack pid |grep tid的十六进制 -A 30 查看堆栈信息定位

jvm old区占用过高排查思路

  1. top查看占用cpu高的进程
  2. jstat -gcutil pid 时间间隔 查看gc状况
  3. jmap -dump:format=b,file=name.dump pid  导出dump文件
  4. 用jvisualVM分析dump文件

一、空间查看

1.1 磁盘空间

df --help 查看命令使用帮助说明


df -h #查看磁盘空间大小

du -sh #查看当前文件夹所有文件大小

du -h /home #查看指定文件夹大小

du -h /home/ #查看指定文件夹下所有文件的大小

du -h login.log #查看指定文件大小

df /home #查看目录挂载点

df /home -kh #加上-kh以g单位显示

du -sh ./* #统计当前目录各文件夹大

du /home/ -h --max-depth=1 #查看home文件夹下文件大小

1.2 内存空间

free [options],默认情况下,即在没有选项的情况下,"free"命令显示内存的使用信息。默认按照k(b)的计数单位统计。

free -b/k/m/g/h ( free -h:以适于人类可读方式显示内存信息。-h与其他命令最大不同是-h选项会在数字后面加上适于人类可读的单位)

free -s N:表示每隔n秒打印一次内存信息,直到用ctrl+c结束

free -c N:表示重复打印内存信息n次

free -hs N: 以人类可读的方式,每隔ns打印一次内存信息,直到ctrl+c结束

free -s N -c M:每隔n秒打印一次内存信息,共打印m次
  • total:表示 总计物理内存的大小。
  • used:表示 已使用多少。
  • free:表示 可用内存多少。
  • Shared:表示多个进程共享的内存总额。
  • Buffers/cached:表示 磁盘缓存的大小。

二、进程确定

2.1 查看比较耗内存的进程

top //shift+m 按内存占用比排序

参数含义

第一行:
21:31:22 当前系统时间
up 16 days,  6:03 系统已经运行了16天6小时03分钟(在这期间没有重启过)
2 users 当前有2个用户登录系统
load average: 0.03, 0.06, 0.05  load average后面的三个数分别是1分钟、5分钟、15分钟的负载情况。load average数据是每隔5秒钟检查一次活跃的进程数,然后按特定算法计算出的数值。如果这个数除以逻辑 CPU的数量,结果高于5的时候就表明系统在超负荷运转了。

第二行: Tasks 任务(进程)
系统现在共有87个进程,其中 running(运行) 2个,sleep(休眠)85 个,stopped 0个,zombie(僵尸) 1个。

第三行:cpu状态
2.7% us 用户空间占用CPU的百分比。
3.0% sy 内核空间占用CPU的百分比。
0.0% ni 改变过优先级的进程占用CPU的百分比
94.4% id 空闲CPU百分比
0.0% wa IO等待占用CPU的百分比
0.0% hi 硬中断(Hardware IRQ)占用CPU的百分比
0.0% si 软中断(Software Interrupts)占用CPU的百分比

第四行:内存状态
1014864k total 物理内存总量(991M)
80320k free 空闲内存总量(79M)
753356k used 使用中的内存总量(735M)
179808k buffers/cached 缓存的内存量 (175M)

使用中的内存总量(used)指的是现在系统内核控制的内存数,空闲内存总量(free)是内核还未纳入其管控范围的数量。纳入内核管理的内存不见得都在使用中,还包括过去使用过的现在可以被重复利用的内存,内核并不把这些可被重新使用的内存交还到free中去,因此在linux上free内存会越来越少,但不用为此担心。

如果出于习惯去计算可用内存数,这里有个近似的计算公式:第四行的free + 第四行的buffers + 第五行的cached,按这个公式此台服务器的可用内存: 79 +175 + 99 = 353M。

对于内存监控,在top里我们要时刻监控第五行swap交换分区的used,如果这个数值在不断的变化,说明内核在不断进行内存和swap的数据交换,这是真正的内存不够用了。

第五行:swap交换分区
0k total 交换区总量(0M)
0k used 使用的交换区总量(0M)
0k free 空闲交换区总量(0M)
101588k avail Mem 缓冲的交换区总量(99M)

第七行以下:各进程(任务)的状态监控
PID 进程id
USER 进程所有者
PR 进程优先级
NI nice值。负值表示高优先级,正值表示低优先级
VIRT 进程使用的虚拟内存总量,单位kb。VIRT=SWAP+RES
RES 进程使用的、未被换出的物理内存大小,单位kb。RES=CODE+DATA
SHR 共享内存大小,单位kb
S 进程状态。D=不可中断的睡眠状态 R=运行 S=睡眠 T=跟踪/停止 Z=僵尸进程
%CPU 上次更新到现在的CPU时间占用百分比
%MEM 进程使用的物理内存百分比
TIME+ 进程使用的CPU时间总计,单位1/100秒
COMMAND 进程名称(命令名/命令行)

2.2 查看java进程

1.jps:虚拟机进程状况工具

jps主要用来输出JVM中运行的进程状态信息。语法格式如下:
jps [options] [hostid]

第一个参数:options
   -q 不输出类名、Jar名和传入main方法的参数
   -m 输出传入main方法的参数
   -l 输出main类或Jar的全限名
   -v 输出传入JVM的参数

第二个参数:hostid
主机或者是服务器的id,如果不指定,就默认为当前的主机或者是服务器。

2.ps -ef|grep tomcat 或者 ps -aux|grep tomcat

三、jvm 性能监控工具
       工具主要是为了解决问题而生的,就是由于我们的程序存在着一些性能问题,才有了这些工具。其实当我们在下载完成JDK,里面就包含了一些性能检测工具,在JDK安装目录下有很多性能监控工具。

  1. jps:虚拟机进程状况工具
  2. jstat:虚拟机统计信息监视工具
  3. jmap:Java内存印象工具
  4. jhat:虚拟机堆转储快照分析工具
  5. jstack:Java堆栈跟踪工具
  6. jinfo:Java配置信息工具

1、jps:虚拟机进程状况工具
jps主要用来输出JVM中运行的进程状态信息。语法格式如下:
jps [options] [hostid]
第一个参数:options
-q 不输出类名、Jar名和传入main方法的参数
-m 输出传入main方法的参数
-l 输出main类或Jar的全限名
-v 输出传入JVM的参数
第二个参数:hostid
主机或者是服务器的id,如果不指定,就默认为当前的主机或者是服务器。

2、jstack:堆栈跟踪工具
jstack用于生成虚拟机当前时刻的线程快照。语法格式如下:
jstack [option] vmid
第一个参数:option
第二个参数:vmid
vmid是Java虚拟机ID,在Linux/Unix系统上一般就是进程ID。


3、jstat:虚拟机统计信息监控工具
jstat监视虚拟机各种运行状态信息,可以显示本地或者是远程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。语法格式如下:
jstat [ generalOption | outputOptions vmid [interval] [count]] ]
第一个参数:generalOption | outputOptions
这个参数表示的option,代表着用户希望查询的虚拟机信息,分为类加载、垃圾收集、运行期编译状况3类。
第二个参数:vmid
vmid是Java虚拟机ID,在Linux/Unix系统上一般就是进程ID。
第三个参数:interval
interval是采样时间间隔,
第四个参数:count
count表示的是采样数。

4、jinfo:实时地查看和调整虚拟机各项参数
命令格式:
jinfo [option] pid
第一个参数:option
第二个参数:pid
指定显示的进程id。

5、jmap:生成虚拟机的内存转储快照(heapdump文件)
jmap(Memory Map for Java,内存映像工具),用于生成堆转存的快照,一般是heapdump或者dump文件。如果不适用jmap命令,可以使用-XX:+HeapDumpOnOutOfMemoryError参数,当虚拟机发生内存溢出的时候可以产生快照。或者使用kill -3 pid也可以产生。jmap的作用并不仅仅是为了获取dump文件,它可以查询finalize执行队列,java堆和永久代的详细信息,如空间使用率,当前用的哪种收集器。命令格式如下:
jmap [option] vmid
第一个参数:
第二个参数:vmid
vmid是Java虚拟机ID,在Linux/Unix系统上一般就是进程ID.

6、jhat:分析内存转储快照,不推荐使用,而且慢
由于这个工具功能比较简陋,运行起来也比较耗时,所以这个工具不推荐使用,推荐使用MAT。

7、JConsole:JMX的可视化管理工具
这个工具相比较前面几个工具,使用率比较高,很重要。它是一个java GUI监视工具,可以以图表化的形式显示各种数据。并可通过远程连接监视远程的服务器VM。用java写的GUI程序,用来监控VM,并可监控远程的VM,非常易用,而且功能非常强。
在cmd里面输入 jconsole,选则进程就可以了。(前提是在IDE工具先建立一个线程运行着)
然后我们选择了相应的选项之后,进入这个工具就会出现下面这个界面
在上面有菜单,我们可以选择其中一个进行查看,就可以了,这个用具用起来很方便,也是我之前用的比较多的工具。

8、VisualVM:多合一故障管理工具
这个工具也很牛bility。它同jconsole都是一个基于图形化界面的、可以查看本地及远程的JAVA GUI监控工具,Jvisualvm同jconsole的使用方式一样,直接在命令行打入jvisualvm即可启动,jvisualvm界面更美观一些,数据更实时。

四、jvm调优

4.1 配置参数

在tomcat/bin/catalina.sh的配置开始位置添加:

JAVA_OPTS="-Djava.awt.headless=true -XX:+PrintGCDetails -Dfile.encoding=UTF-8 -server -Xms128m -Xmx512m -XX:NewSize=1m -XX:MaxNewSize=256m -XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=256m"

参数说明:

-Xms 初始堆的分配大小,默认为物理内存的六十四分之一
-Xmx 堆的最大分配大小(默认为物理内存的四分之一),在实际工作中,可以直接将初始的堆大小与最大堆大小相等,减少程序运行时垃圾回收的次数,提高效率。
如:-Xms512m -Xmx512m
-Xmn 新生代堆最大可用值(默认为堆内存的64分之一)
-XX:NewSize和-XX:MaxNewSize 用于设置年轻代的大小,建议设为整个堆大小的1/3或者1/4,两个值设为一样大。
-XX:+PrintGC 每次触发GC的时候打印相关日志
-XX:+PrintGCDetails 打印详细的GC日志
-XX:+UseSerialGC 串行回收
-XX:SurvivorRatio
含以-XX:SurvivorRatio=eden/from=den/to

用来设置新生代中rom:to:eden空间的比例 (默认1:1:8,即-XX:SurvivorRatio=8)

在实际工作中,我们可以直接将初始的堆大小与最大堆大小相等,这样的好处是可以减少程序运行时垃圾回收次数,从而提高效率。

-XX:NewRatio

设置年轻代和年老代的比值(默认为2)。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4

-Xss 每个线程堆栈的大小。一般情况下256K是足够了,会影响此进程中并发线程数大小。

JVM参数调优总结
在JVM启动参数中,可以设置跟内存、垃圾回收相关的一些参数设置,默认情况不做任何设置JVM会工作的很好,但对一些配置很好的Server和具体的应用必须仔细调优才能获得最佳性能。通过设置我们希望达到一些目标:

  • GC的时间足够的小
  • GC的次数足够的少
  • 发生Full GC的周期足够的长

前两个目前是相悖的,要想GC时间小必须要一个更小的堆,要保证GC次数足够少,必须保证一个更大的堆,我们只能取其平衡。

  • 针对JVM堆的设置,一般可以通过-Xms -Xmx限定其最小、最大值,为了防止垃圾收集器在最小、最大之间收缩堆而产生额外的时间,我们通常把最大、最小设置为相同的值。
  • 年轻代和年老代将根据默认的比例(1:2)分配堆内存,可以通过调整二者之间的比率NewRadio来调整二者之间的大小,也可以针对回收代,比如年轻代,通过 -XX:newSize -XX:MaxNewSize来设置其绝对大小。同样,为了防止年轻代的堆收缩,我们通常会把-XX:newSize -XX:MaxNewSize设置为同样大小
  • 年轻代和年老代设置多大才算合理?这个我问题毫无疑问是没有答案的,否则也就不会有调优。我们观察一下二者大小变化有哪些影响:
  1. 更大的年轻代必然导致更小的年老代,大的年轻代会延长普通GC的周期,但会增加每次GC的时间;
  2. 小的年老代会导致更频繁的Full GC,更小的年轻代必然导致更大年老代,小的年轻代会导致普通GC很频繁,但每次的GC时间会更短;大的年老代会减少Full GC的频率。
  3. 如何选择应该依赖应用程序对象生命周期的分布情况:如果应用存在大量的临时对象,应该选择更大的年轻代;如果存在相对较多的持久对象,年老代应该适当增大。

但很多应用都没有这样明显的特性,在抉择时应该根据以下两点:(A)本着Full GC尽量少的原则,让年老代尽量缓存常用对象,JVM的默认比例1:2也是这个道理 (B)通过观察应用一段时间,看其他在峰值时年老代会占多少内存,在不影响Full GC的前提下,根据实际情况加大年轻代,比如可以把比例控制在1:1。但应该给年老代至少预留1/3的增长空间。

4.2 怎么样避免发生内存泄露和溢出

  • a、尽早释放无用对象的引用
  • b、使用字符串处理,避免使用String,应大量使用StringBuffer,每一个String对象都得独立占用内存一块区域
  • c、尽量少用静态变量,因为静态变量存放在永久代(方法区),永久代基本不参与垃圾回收
  • d、避免在循环中创建对象
  • e、开启大型文件或从数据库一次拿了太多的数据很容易造成内存溢出,所以在这些地方要大概计算一下数据量的最大值是多少,并且设定所需最小及最大的内存空间值。

猜你喜欢

转载自blog.csdn.net/u014553029/article/details/105958812