暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

浅谈Linux系统资源监控

新大陆软件评测中心 2019-01-04
530


浅谈Linux系统资源监控



引言

在java测试过程中,你有没有遇到过这样的问题:

  1. OutOfMemoryError,内存不足

  2. 内存泄露

  3. 线程死锁

  4. Java进程消耗CPU过高

这些问题在日常工作中可能会被很多人忽略,比如有的人遇到上面的问题,只是通过重启服务器或者调大内存来临时解决,并不会深入研究问题根源,而往往这些问题在生产环境中,却会导致严重的后果:服务器宕机,事务无法正常执行,进而导致严重的故障。因此,理解并解决这些问题成为测试工作中的必备要求。


今天,就给大家简单介绍一下如何使用linux nmon监控linux系统资源,并使用Java VisualVM工具查看是否存在内存泄露,当确定系统存在内存泄露时,如何使用MAT(Memory Analyzer Tools)进行内存泄漏原因分析,结合实际工作中遇到的问题,给大家介绍下这几个工具的使用。


1

nmon监控linux系统资源

1.1 什么是nmon?

Nmon是IBM提供的一款在AIX与各类Linux操作系统上广泛使用的监控与分析工具,该工具可以将服务器的系统资源使用情况收集起来并输出到特定的文件,并结合excel 分析工具nmon analyser 可视化展现,利于进行数据的统计分析。

其优点是运行起来不会占用过多的系统资源,通常情况下CPU利用率不超过2%。


1.2 如何获取nmon?

针对不同的操作系统版本,nmon有相应版本的程序。

下载页面:http://nmon.sourceforge.net/pmwiki.php?n=Site.Download

官方网站:http://nmon.sourceforge.net/

根据系统的发行版本及CPU位数选择相应的压缩包下载,如笔者的系统发行版本为:红帽子redhat 6.2,64位:

所以选择的下载版本为:nmon16g_x86.tar.gz


1.3 安装nmon

特别说明:以下操作均需使用root帐号进行操作

Root用户创建一个文件夹nmon,并将下载下来的nmon16g_x86.tar.gz FTP到nmon文件夹下,然后进行解压,解压出来的有适用于不同linux发行版本的文件,根据自身系统选择不同文件,这里笔者选择的是nmon_x86_64_rhel6:

这时候我们迫不及待的想使用它,但是执行却出错了,提示“Permission denied”,原因是我们默认下载的文件权限为664,没有执行权限,所以我们需要添加执行权限。

这时候我们就可以愉快的使用nmon了。那怎么用呢?跟着文章一步一步来:

执行nmon:./nmon,进入nmon实时监控页,按c查看CPU使用信息,按m查看内存使用信息,按n查看网络使用信息:

假如我们希望每3秒采集一次数据,总共采集60次,并以文件的方式保存下来,可以执行如下命令:

[root@localhost nmon]#  ./nmon -s3 -c60 -f -m ./report

参数解释:

-s3 每 3 秒采集一次数据。

-c60 采集 60 次,即为采集3分钟的数据。

-f 生成的数据文件名中包含文件创建的时间。如:<hostname>_YYYYMMDD_HHMM.nmon

-m 生成的数据文件的存放目录。

nmon -h查看更多帮助信息。

执行完收集信息的命令后,生成了以hostname+创建年月+创建时间的文件,并且可以看到有个nmon的后台进程,即为收集服务器性能信息的进程,直到60次收集执行完之后,该进程才会结束。

到此,我们完成了数据的采集工作,很开心,但是当我们把生成的文件下载下来的时候,却犯难了,这个文件如何打开,这里就涉及到了 nmon analyser分析工具,这个工具能将上面生成的.nmon文件转化为可视化图表,非常便于分析。

下载地址:

https://www.ibm.com/developerworks/community/wikis/home?lang=en#!/wiki/Power%20Systems/page/nmon_analyser

下载完解压可以看到有两个文件:

打开 nmon analyser v55.xlsm文件:

将linux服务器上的报告.nmon文件下载下来,点击上图中的Analyze nmon data按钮,选择刚刚下载下来的文件,生成可视化图表,下面先让我们饱下眼福,看下nmon analyser生成的报表:

  • 服务器资源使用率汇总

  • 磁盘读写I/O相关图

  • 内存使用率

  • 网络带宽使用情况

至此,我们已经可以很轻松的对系统执行主机进行资源监控了,但是有人就会产生疑问了,通过nmon文件,我只知道资源的使用情况,那我怎么知道这个资源使用有没有问题呢?是否存在内存泄露呢?不要着急,我们接着往下看:


2

Java VisualVM工具的使用

2.1 什么是Java VisualVM?

Java VisualVM是JDK自带的一款全能型性能监控和故障分析工具,包括对CPU使用、JVM堆内存消耗、线程、类加载的实时监控,内存dump文件分析,垃圾回收运行情况的可视化分析等,对故障排查和性能调优很有帮助。在系统中安装JDK后,VisualVM位于%JAVA_HOME%/bin/下,直接执行jvisualvm.exe即可,这里以笔者本机的目录进行解说:

直接执行jvisualvm.exe,运行的界面如下:

JVM从界面上看还是比较简洁的,左边是树形结构,自动显示当前本机所运行的Java程序,还可以添加远程的Java VM,其中括号里面的PID指的是进程ID。概述界面显示VM启动参数以及该VM对应的一些属性。监控界面则是监控Java堆大小,Permgen大小,Classes和线程数量。

JVM连接远程服务器有两种方式:JMX和jstatd,两种方式都不能完美支持所有功能,例如JMX不支持VisualGC,jstatd不支持CPU监控,实际使用可同时配置上并按需选用。


2.2 使用JMX远程监控web程序

2.2.1 配置前的准备

开始进行配置前,我们首先需要检查linux的hostname匹配的IP,在需要进行监控的主机上,执行如下命令

[jutap@bass-testetl-app2 ~]$ hostname -i

127.0.0.1 127.0.0.1

[jutap@bass-testetl-app2 ~]$

如果匹配的结果是无法识别或者127.0.0.1,则需要手动配置主机名和IP映射(此操作需要root帐号执行)

假设要用于建立连接的IP是10.1.8.97,主机名是mylinux:

vi /etc/hosts

添加一行

10.1.8.97  mylinux


2.2.2 配置JVM

在服务器 上的 tomcat 配置 jvm 启动参数,

在tomcat 的 catalina.sh 中添加如下参数:

JAVA_OPTS="$JAVA_OPTS -Djava.rmi.server.hostname=10.1.8.97                                    -Dcom.sun.management.jmxremote.port=13668
                        -Dcom.sun.management.jmxremote.ssl=false 
                        -Dcom.sun.management.jmxremote.authenticate=false"  

上述参数未设用户名与密码登录。

添加的位置如下:

注:hostname为要监控的主机IP,port为监听端口,可以设置一个未被占用的端口


2.2.3 关闭防火墙

以上配置是需要关闭防火墙。

关闭防火墙命令

service iptables stop

重启防火墙命令

service iptables restart

注意:如果想不关闭防火墙来访问。需要在iptables里把刚才配置的端口公布出去:

在etc/sysconfig/iptables中加入:

vi /etc/sysconfig/iptables


-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 13668-j ACCEPT


-A INPUT -m state --state NEW -m tcp -p tcp --dport 13668-j ACCEPT

到此,准备工具就已经做好了,可以打开客户端,远程监控了。


2.2.4 客户端java VisualVM启动监控

(1)打开java安装目录bin目录下的jvisualvm.exe

(2)在远程上右键,添加远程主机

(3)在添加的远程主机上右键,添加Jmx,此处不需要密码,点击确定即可。

(4)双击连接查看Jvm运行情况(其中括号里面的PID指的是进程ID)


2.3 使用Jstatd远程监控服务器

2.3.1 什么是Jstatd?

Jstatd是一个基于RMI(Remove Method Invocation)的服务程序,它用于监控基于HotSpot的JVM中资源的创建及销毁,并且提供了一个远程接口允许远程的监控工具连接到本地的JVM执行命令。可以实时显示本地或远程JVM进程中类装载、内存、垃圾收集、JIT编译等数据。


2.3.2 启动RMI服务

在需要被监控的服务器上面,通过jstatd来启动RMI服务。

首先,配置java安全访问,在服务器jdk路径($JAVA_HOME/bin)下 创建jstatd服务器授权文件jstatd.all.policy,其内容:

grant codebase "file:${java.home}/../lib/tools.jar" {

    permission java.security.AllPermission;

};

然后在进入jstatd.all.policy所在目录下,通过如下的命令启动RMI服务:

./jstatd -J-Djava.security.policy=jstatd.all.policy

如若出现端口占用问题,在启动jstatd服务器时选择其它端口。

//查看端口是否被占用

netstat -ano | grep -i 1099

//启动jstatd服务器时,指定其它端口

rmiregistry 2030 & jstatd -J-Djava.security.policy=jstatd.all.policy -p 2030

2.3.3 启动客户端的VisualVM,添加远程主机

进入$JAVA_HOME/bin目录下启动VisualVM,,在VisualVM添加Jstatd服务器地址,就会自动连接成功,并获取服务器的CPU,内存,线程等信息。

客户端连接成功后,界面展示如下:

以上介绍的两种连接办法都可以用来实时监控服务器java内存的情况,通过监控线程,内存情况,查看方法的CPU时间和内存中的对象,已被GC的对象,反向查看分配的堆栈,可以发现是否存在java内存溢出的问题。

下面结合笔者实际项目中遇到的问题,配合工具的使用,介绍一下如何使用工具来定位问题。笔者最近一个项目,在长时间运行后,系统就会变得越来越慢,甚至到后面,事务无法正常执行,查看JVM情况,发现页面即使长时间没有请求了,内存也不会回收,下图所示,我们可以初步定位系统发生了内存泄露。

以上我们是使用JDK自带的工具去发现是否存在内存泄露,但是当判断有内存泄露存在时,试图要去寻找内存泄露的点时,发现单纯使用JDK自身提供的工具并没有很好的办法,这时候我们就需要MAT(Memory Analyzer Tool)工具,那MAT是什么呢?


3

Memory Analyzer Tool分析内存泄露点

3.1 什么是MAT?

Memory Analyzer是一个功能丰富且轻量的Java堆内存分析工具,可以用来辅助发现内存泄露减少内存占用。

使用Memory Analyzer来分析生产环境的Java堆转储文件,可以从数以百万计的对象中快速计算出对象的Retained Size,查看是谁在阻止垃圾回收,并自动生成一个Leak Suspect(内存泄露可疑点)报表。


3.2 MAT分析内存泄露点

在分析之前,我们首先应该了解一个概念: Heap Dump。

什么是 Heap Dump呢?

Heap Dump是一个Java进程在某个时间点上的内存快照,保存了java对象和类的信息。通常在写Heap Dump文件前会触发一次FullGC(清理整个堆空间),所有Heap Dump文件中保存的是FullGC后留下的对象信息。

Memory Analyzer可以用来处理Dump文件,从Dump文件中,我们可以获取到对象信息、类信息、对象、线程栈以及本地变量,通过对以上信息的分析,基本可以定位内存泄露的产生点。


3.2.1 安装MAT

可以选择eclipse插件的方式安装

http://download.eclipse.org/mat/1.3/update-site/


也可以选择单独MAT程序下载安装

http://www.eclipse.org/mat/downloads.php


3.2.2 分析DUMP文件

用MAT打开 dump(如果是MAT dump完后会自动进行解析),File->Open Heap Dump 对dump文件进行解析,最终生成一个Overview视图,这个图是一个概要图,显示了一些统计信息,包括整个size大小,class数量,以及对象 的数量,同时还将生成一个大对象的top图,并显示大对象占用内存的百分比。

3.2.3 找出溢出源

Histogram视图:列出每個class产生了多少個实例,以及占有多大内存,所占百分比

可以很容易找出站内存最多的几个类,根据Retained Heap排序,找出前几个。

可以分不同的维度来查看类的Histogram视图,Group by class、Group by superclass、Group by class  loader、Group by package

只要有溢出,时间久了,溢出类的实例数量或者其占有的内存会越来越多,排名也就越来越前,通过多次对比不同时间点下的Histogram图对比就能很容易把溢出类找出来。

3.2.4 定位溢出的原因

通过Histogram视图或者Dominator Tree视图,找到疑似溢出的对象或者类后,选择Path to GC Roots或者Merge Shortest Paths to GC Roots,这里有很多过滤选项,一般来讲可以选择exclude all plantom/weak/soft etc. references。这样就排除了虚引用、弱引用、以及软引用,剩下的就是强引用。从GC上说,除了强引用外,其他的引用在JVM需要的情况下是都可以 被GC掉的,如果一个对象始终无法被GC,就是因为强引用的存在,从而导致在GC的过程中一直得不到回收,因此就内存溢出了。


以下使用笔者最近项目遇到的一个内存泄露的案例进行分析。

  • 问题定位

使用MAT分析dump文件,可以发现org.apache.commons.dbcp.PoolingConnection对象占用了整个堆内存的59.73%,并且这个对象的shallow heap和retained heap是非常不成比例的,这说明这个此对象持有了大量对象的引用,于是我们根据retained heap的大小逐步对支配树进行展开可以发现,org.apache.commons.dbcp.PoolingConnection下面持有大量的 oracle.jdbc.driver.T4CPreparedStatement对象的引用,数量高达3423之多,每个T4CPreparedStatement的Shallow Heap为1128Bytes,retained heap为100左右KB,3423个则占了300多M,总共内存500M,因此,从这里就可以断定T4CPreparedStatement使用不当出现了内存泄漏。

  • 问题分析

分析oracle.jdbc.driver.T4CPreparedStatement,可以将下图看到的预览中的sql复制出来。发现里面的sql都是前端某个菜单高级查询的sql,该功能提供用户编辑SQL,由用户自己定义where之后的条件。

  • 问题定位

高级查询功能,允许用户自由书写where之后的条件,因为每次查询的条件都不一样,oracle的预编译功能会将每次不一样的sql缓存起来。当超过一定数量的个数时,堆内存空间不足,导致内存溢出

  • 问题重现及解决

根据以上定位的问题原因,我们进行测试模拟,发现在测试开始时,JVM的内存会释放,但是连续执行几个小时后,内存发生溢出,该结论验证我们定位的方向是正确的。开发根据此原因有针对性的进行问题解决,重新设置oracle驱动的内存管理机制,当设置 oracle.jdbc.freeMemoryOnEnterImplicitCache=true,可以不缓存那个对象,此修改经验证有效果。



结束语

以上介绍JDK本身提供的JVM性能调优监控工具,当然除了本文列的之外,还有jps、jstack、jmap、jhat等等很多小巧而又方便的工具。

笔者希望此文能起到抛砖引玉之用,让大家能对JVM性能调优常用工具有所了解,也希望此文提到的方法能对大家平时工作有帮助,感谢大家的阅读。


新大陆软件评测中心

地址:福州市经济开发区儒江大道1号新大陆科技园B楼3层

邮箱:nlsetc@newland.com.cn

电话:0591-8397 9159

专业测试,成就卓越

文章转载自新大陆软件评测中心,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论