重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
1、为什么会发生内存泄漏
创新互联建站专业为企业提供喀什网站建设、喀什做网站、喀什网站设计、喀什网站制作等企业网站建设、网页设计与制作、喀什企业网站模板建站服务,10余年喀什做网站经验,不只是建网站,更提供有价值的思路和整体网络服务。
Java如何检测内在泄漏呢?我们需要一些工具进行检测,并发现内存泄漏问题,不然很容易发生down机问题。
编写java程序最为方便的地方就是我们不需要管理内存的分配和释放,一切由jvm来进行处理,当java对象不再被应用时,等到堆内存不够用时,jvm会进行垃圾回收,清除这些对象占用的堆内存空间,如果对象一直被应用,jvm无法对其进行回收,创建新的对象时,无法从Heap中获取足够的内存分配给对象,这时候就会导致内存溢出。而出现内存泄露的地方,一般是不断的往容器中存放对象,而容器没有相应的大小限制或清除机制。容易导致内存溢出。
当服务器应用占用了过多内存的时候,如何快速定位问题呢?现在,Eclipse MAT的出现使这个问题变得非常简单。EclipseMAT是著名的SAP公司贡献的一个工具,可以在Eclipse网站下载到它,完全免费的。
要定位问题,首先你需要获取服务器jvm某刻内存快照。jdk自带的jmap可以获取内存某一时刻的快照,导出为dmp文件后,就可以用Eclipse MAT来分析了,找出是那个对象使用内存过多。
2、内存泄漏的现象:
常常地,程序内存泄漏的最初迹象发生在出错之后,在你的程序中得到一个OutOfMemoryError。这种典型的情况发生在产品环境中,而在那里,你希望内存泄漏尽可能的少,调试的可能性也达到最小。也许你的测试环境和产品的系统环境不尽相同,导致泄露的只会在产品中暴露。这种情况下,你需要一个低负荷的工具来监听和寻找内存泄漏。同时,你还需要把这个工具同你的系统联系起来,而不需要重新启动他或者机械化你的代码。也许更重要的是,当你做分析的时候,你需要能够同工具分离而使得系统不会受到干扰。
一个OutOfMemoryError常常是内存泄漏的一个标志,有可能应用程序的确用了太多的内存;这个时候,你既不能增加JVM的堆的数量,也不能改变你的程序而使得他减少内存使用。但是,在大多数情况下,一个OutOfMemoryError是内存泄漏的标志。一个解决办法就是继续监听GC的活动,看看随时间的流逝,内存使用量是否会增加,如果有,程序中一定存在内存泄漏。
3、发现内存泄漏
1. jstat -gc pid
可以显示gc的信息,查看gc的次数,及时间。
其中最后五项,分别是young gc的次数,young gc的时间,full gc的次数,full gc的时间,gc的总时间。
2.jstat -gccapacity pid
可以显示,VM内存中三代(young,old,perm)对象的使用和占用大小,
如:PGCMN显示的是最小perm的内存使用量,PGCMX显示的是perm的内存最大使用量,
PGC是当前新生成的perm内存占用量,PC是但前perm内存占用量。
其他的可以根据这个类推,OC是old内纯的占用量。
3.jstat -gcutil pid
统计gc信息统计。
4.jstat -gcnew pid
年轻代对象的信息。
5.jstat -gcnewcapacity pid
年轻代对象的信息及其占用量。
6.jstat -gcold pid
old代对象的信息。
7.stat -gcoldcapacity pid
old代对象的信息及其占用量。
8.jstat -gcpermcapacity pid
perm对象的信息及其占用量。
9.jstat -class pid
显示加载class的数量,及所占空间等信息。
10.jstat -compiler pid
显示VM实时编译的数量等信息。
11.stat -printcompilation pid
当前VM执行的信息。
一些术语的中文解释:
S0C:年轻代中第一个survivor(幸存区)的容量(字节)
S1C:年轻代中第二个survivor(幸存区)的容量(字节)
S0U:年轻代中第一个survivor(幸存区)目前已使用空间(字节)
S1U:年轻代中第二个survivor(幸存区)目前已使用空间(字节)
EC:年轻代中Eden(伊甸园)的容量(字节)
EU:年轻代中Eden(伊甸园)目前已使用空间(字节)
OC:Old代的容量(字节)
OU:Old代目前已使用空间(字节)
PC:Perm(持久代)的容量(字节)
PU:Perm(持久代)目前已使用空间(字节)
YGC:从应用程序启动到采样时年轻代中gc次数
YGCT:从应用程序启动到采样时年轻代中gc所用时间(s)
FGC:从应用程序启动到采样时old代(全gc)gc次数
FGCT:从应用程序启动到采样时old代(全gc)gc所用时间(s)
GCT:从应用程序启动到采样时gc用的总时间(s)
NGCMN:年轻代(young)中初始化(最小)的大小(字节)
NGCMX:年轻代(young)的最大容量(字节)
NGC:年轻代(young)中当前的容量(字节)
OGCMN:old代中初始化(最小)的大小(字节)
OGCMX:old代的最大容量(字节)
OGC:old代当前新生成的容量(字节)
PGCMN:perm代中初始化(最小)的大小(字节)
PGCMX:perm代的最大容量(字节)
PGC:perm代当前新生成的容量(字节)
S0:年轻代中第一个survivor(幸存区)已使用的占当前容量百分比
S1:年轻代中第二个survivor(幸存区)已使用的占当前容量百分比
E:年轻代中Eden(伊甸园)已使用的占当前容量百分比
O:old代已使用的占当前容量百分比
P:perm代已使用的占当前容量百分比
S0CMX:年轻代中第一个survivor(幸存区)的最大容量(字节)
S1CMX:年轻代中第二个survivor(幸存区)的最大容量(字节)
ECMX:年轻代中Eden(伊甸园)的最大容量(字节)
DSS:当前需要survivor(幸存区)的容量(字节)(Eden区已满)
TT:持有次数限制
MTT:最大持有次数限制
如果定位内存泄漏问题我一般使用如下命令:
Jstat -gcutil15469 2500 70
[root@ssss logs]# jstat -gcutil 15469 1000 300
S0 S1 E O P YGC YGCT FGC FGCT GCT
0.00 1.46 26.54 4.61 30.14 35 0.872 0 0.000 0.872
0.00 1.46 46.54 4.61 30.14 35 0.872 0 0.000 0.872
0.00 1.46 47.04 4.61 30.14 35 0.872 0 0.000 0.872
0.00 1.46 65.19 4.61 30.14 35 0.872 0 0.000 0.872
0.00 1.46 67.54 4.61 30.14 35 0.872 0 0.000 0.872
0.00 1.46 87.54 4.61 30.14 35 0.872 0 0.000 0.872
0.00 1.46 88.03 4.61 30.14 35 0.872 0 0.000 0.872
1.48 0.00 5.56 4.62 30.14 36 0.874 0 0.000 0.874
1000 代表多久间隔显示一次,
100 代表显示一次。
S0 — Heap上的 Survivor space 0 区已使用空间的百分比
S1 — Heap上的 Survivor space 1 区已使用空间的百分比
E — Heap上的 Eden space 区已使用空间的百分比
O — Heap上的 Old space 区已使用空间的百分比
P — Perm space 区已使用空间的百分比
YGC — 从应用程序启动到采样时发生 Young GC 的次数
YGCT– 从应用程序启动到采样时 Young GC 所用的时间(单位秒)
FGC — 从应用程序启动到采样时发生 Full GC 的次数
FGCT– 从应用程序启动到采样时 Full GC 所用的时间(单位秒)
GCT — 从应用程序启动到采样时用于垃圾回收的总时间(单位秒)
如果有大量的FGC就要查询是否有内存泄漏的问题了,图中的FGC数量就比较大,并且执行时间较长,这样就会导致系统的响应时间较长,如果对jvm的内存设置较大,那么执行一次FGC的时间可能会更长。
如果为了更好的证明FGC对服务器性能的影响,我们可以使用java visualVM来查看一下:
从上图可以发现执行FGC的情况,下午3:10分之前是没有FGC的,之后出现大量的FGC。
上图是jvm堆内存的使用情况,下午3:10分之前的内存回收还是比较合理,但是之后大量内存无法回收,最后导致内存越来越少,导致大量的full gc。
下面我们在看看大量full GC对服务器性能的影响,下面是我用loadrunner对我们项目进行压力测试相应时间的截图:
从图中可以发现有,在进行full GC后系统的相应时间有了明显的增加,点击率和吞吐量也有了明显的下降。所以java内存泄漏对系统性能的影响是不可忽视的。
3、定位内存泄漏
当然通过上面几种方法我们可以发现java的内存泄漏问题,但是作为一名合格的高级工程师,肯定不甘心就把这样的结论交给开发,当然这也的结论交给开发,开发也很难定位问题,为了更好的提供自己在公司的地位,我们必须给开发工程师提供更深入的测试结论,下面就来认识一下MemoryAnalyzer.exe。java内存泄漏检查工具利器。
首先我们必须对jvm的堆内存进行dump,只有拿到这个文件我们才能分析出jvm堆内存中到底存了些什么内容,到底在做什么?
MemoryAnalyzer的用户我在这里就不一一说明了,我的博客里也有说明,下面就展示我测试的成功图:
其中深蓝色的部分就为内存泄漏的部分,java的堆内存一共只有481.5M而内存泄漏的部分独自占有了336.2M所以本次的内存泄漏很明显,那么我就来看看那个方法导致的内存泄漏:
从上图我们可以发现红线圈着的方法占用了堆内存的67.75%,如果能把这个测试结果交给开发,开发是不是应该很好定位呢。所以作为一名高级测试工程师,我们需要学习的东西太多。
虽然不确定一定是内存泄漏,但是可以准确的告诉开发问题出现的原因,有一定的说服力。
先用下面的命令监视进程:
while ( sleep 1 ) ; do ps -p $PID -o %cpu,%mem,rss ; done
如果看到内存上升很快,可能是因为虚拟机设置。如果没有明确指定JVM的内存设置,它将设置默认值给他们。要获得默认值,使用以下命令:
java -XX:+PrintFlagsFinal -version | grep -i HeapSize
如果这些都不符合希望的,那么就需要指定JVM的内存设置。可以用下面的命令设置最小和最大堆大小:
java -Xms128m -Xmx256m
尽管有了合理的内存设置,也可以监控进程,但你仍然可能看到内存随时间增加。为了进一步探究原因,可以使用下面的命令查看对象实例的直方图:
jmap -histo $PID
如果仍然没有足够的信息,那么可以用以下命令进行堆转储:
jmap -dump:format=b,file=/tmp/dump1.hprof $PID
通常,会用两个堆转储,然后使用下面的jhat命令比较它们:
jhat -baseline /tmp/dump1.hprof /tmp/dump2.hprof
这个命令会启动一个HTTP服务器,可以用它来探索这两个堆转储之间的差值。在默认情况下,HTTP服务器启动7000端口,可以在浏览器中访问该端口。
如果有防火墙,可以通过SSH访问,那么可以通过如下命令连接该端口:
ssh -L 7000:localhost:7000 $HOST
Java内存泄露\x0d\x0a一般来说内存泄漏有两种情况。一种情况如在C/C++语言中的,在堆中的分配的内存,在没有将其释放掉的时候,就将所有能访问这块内存的方式都删掉(如指针重新赋值);另一种情况则是在内存对象明明已经不需要的时候,还仍然保留着这块内存和它的访问方式(引用)。第一种情况,在Java中已经由于垃圾回收机制的引入,得到了很好的解决。所以,Java中的内存泄漏,主要指的是第二种情况。\x0d\x0a可能光说概念太抽象了,大家可以看一下这样的例子:\x0d\x0a1Vectorv=newVector(10);\x0d\x0a2for(inti=1;i
回答于 2022-12-14
一般来说内存泄漏有两种情况。一种情况,在堆中的分配的内存,在没有将其释放掉的时候,就将所有能访问这块内存的方式都删掉(如指针重新赋值);另一种情况则是在内存对象明明已经不需要的时候,还仍然保留着这块内存和它的访问方式(引用)。第一种情况,在Java中已经由于垃圾回收机制的引入,得到了很好的解决。所以,Java中的内存泄漏,主要指的是第二种情况。
可能光说概念太抽象了,大家可以看一下这样的例子:
1 Vector v=new Vector(10);
2 for (int i=1;i100; i++){
3 Object o=new Object();
4 v.add(o);
5 o=null;
6 }
在这个例子中,代码栈中存在Vector对象的引用v和Object对象的引用o。在For循环中,我们不断的生成新的对象,然后将其添加到Vector对象中,之后将o引用置空。问题是当o引用被置空后,如果发生GC,我们创建的Object对象是否能够被GC回收呢?答案是否定的。因为,GC在跟踪代码栈中的引用时,会发现v引用,而继续往下跟踪,就会发现v引用指向的内存空间中又存在指向Object对象的引用。也就是说尽管o引用已经被置空,但是Object对象仍然存在其他的引用,是可以被访问到的,所以GC无法将其释放掉。如果在此循环之后,Object对象对程序已经没有任何作用,那么我们就认为此Java程序发生了内存泄漏。
尽管对于C/C++中的内存泄露情况来说,Java内存泄露导致的破坏性小,除了少数情况会出现程序崩溃的情况外,大多数情况下程序仍然能正常运行。但是,在移动设备对于内存和CPU都有较严格的限制的情况下,Java的内存溢出会导致程序效率低下、占用大量不需要的内存等问题。这将导致整个机器性能变差,严重的也会引起抛出OutOfMemoryError,导致程序崩溃。
1、首先我把JVM内存调小,便于在最短的时间内发现问题,利用jstat观察JVM内存回收的情况和使用情况,期间发现旧生代内存的申请在一直进行,但是GC基本回收不回来内存,所以很坚信如果JVM没有BUG的情况下,肯定是存在内存泄漏的地方,应该是代码有问题。但是如何在不翻遍整个代码的情况下,定位问题呢?
2、我查阅几个JVM内存导出工具,并利用JMAP把JVM全部导出来,但是发现悲催的温斗士下,这些工具根本打不开一个G左右的导出文件,直接报乱七八糟的错误,可能也是我的PC硬件配置不高吧,无奈之下只好找了一台Linux服务器,在其上装了MAT工具,然后把JVM导出文件放到这台服务器上进行分析,结果迅速定位到了存在问题的代码
JMAP导出JVM命令格式如下:
jmap -dump:live,format=b,file=heap.bin pid
MAT使用比较简单,不再介绍,只要选择打开导出的文件即可对哪些对象、类等对内存的使用情况一目了然,从而帮助把有可能出问题的代码范围尽量缩小,不用像大海捞针一样采用人海战术逐行代码排查。