1.首先用top查看了下load average,也是很低,也佐证了系统本身压力并不大。
--> TOP命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况。top命令提供了实时的对系统处理器的状态监视.它将显示系统中CPU最“敏感”的任务列表.该命令可以按CPU使用.内存使用和执行时间对任务进行排序;而且该命令的很多特性都可以通过交互式命令或者在个人定制文件中进行设定.
2.利用jstack -l <pid> 查看那些cpu使用率过高的线程,看是否大多数是gc线程,如果是说明gc过于频繁,而且耗时过长,导致应用线程被挂起,无法响应客户端发来的请求,这种情况就应该是内存泄露的问题了。
3.在第二步确定是gc愿意的基础上,用jmap讲进程的堆转储文件dump出来到heap.bin文件中
JMap -dump:format=b,file=/tmp/heap.bin <pid>
然后用Eclipse Memory Analyzer (MAT)打开堆转储文件进行分析:(三部曲)
(1)对问题发生时刻的系统内存状态获取一个整体印象;
(2)找到最有可能导致内存泄露的元凶,通常也就是消耗内存最多的对象
(3)进一步看这个内存消耗大户的具体情况,看是否有异常行为。
从jvm内存泄露问题的发生来看jvm gc:java的一个重要优点就是通过gc自动管理内存回收,程序员不需要通过调用函数来释放内存。但jvm也存在内存泄露,只是和c++不同。
java的内存管理师对象的分配和释放问题,在java中,程序员使用new为每个对象申请内存空间(基本类型除外),所有的对象都在堆(heap)中分配空间。另外,对象的释放都是由gc决定和执行的--收支两条线:减轻程序员的工作,但是家中了JVM的工作。这也是java程序运行速度较慢的原因之一。因为GC为了能够正确释放对象,GC必须监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等,GC都要监控。
监视对象状态是为了更加准确地、及时地释放对象,而释放对象的根本原则就是该对象不再被引用。
为了更好理解GC的工作原理,我们可以将对象考虑为有向图的顶点,将引用关系考虑为图的有向边,有向边从引用者指向被引对象。另外,每个线程对象可以作为一个图的起始顶点,例如大多程序从main进程开始执行,那么该图就是以main进程顶点开始的一棵根树。在这个有向图中,根顶点可达的对象都是有效对象,GC将不回收这些对象。如果某个对象 (连通子图)与这个根顶点不可达(注意,该图为有向图),那么我们认为这个(这些)对象不再被引用,可以被GC回收。
以下,我们举一个例子说明如何用有向图表示内存管理。对于程序的每一个时刻,我们都有一个有向图表示JVM的内存分配情况。以下右图,就是左边程序运行到第6行的示意图。
下面,我们就可以描述什么是内存泄漏。在Java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是可达的,即在有向图中,存在通路可以与其相连;其次,这些对象是无用的,即程序以后不会再使用这些对象。如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存。
下面给出了一个简单的内存泄露的例子。在这个例子中,我们循环申请Object对象,并将所申请的对象放入一个Vector中,如果我们仅仅释放引用本身,那么Vector仍然引用该对象,所以这个对象对GC来说是不可回收的。因此,如果对象加入到Vector后,还必须从Vector中删除,最简单的方法就是将Vector对象设置为null。
Vector v=new Vector(10);
for (int i=1;i<100; i++)
{
Object o=new Object();
v.add(o);
o=null; //仅仅释放引用本身,Vector仍然引用该对象
}