‘壹’ 如何监测windows服务器的性能
Windows服务器中自带的性能监控工具叫做Performance Monitor;
在开始-运行中输入‘perfmon’,然后回车即可运行。
Monitor本身也是一个进程,运行起来也要占用一定的系统资源。所以你看到的资源的使用量应该比实际的要稍微高一点。这个工具在帮助管理员判断系统性能瓶颈时非常有用;
举个列子来说,今天有个用户抱怨说他们项目组的服务器(这是一台虚拟机)运行消告哪起来非常慢,但也不知道具体问题出在什么地方。任务管理器里显示CPU和内存的使用量都不算高,但服务器的相应就是非常慢;
Monitor,让拿码其运行一段时间后(因为参考平均值会比较准确),发现average disk queue的值比较高,这就说明物理服务器的硬盘负荷太重,I/O操作的速度跟不上系统的要求。关掉虚拟机,将其转移到另一台硬盘负载比较小的主机上,再打开虚拟机。
‘贰’ 濡备綍妫娴嫔唴瀛樻硠婕
鍐呭瓨娉勬纺鎸囩敱浜庣枏蹇芥垨阌栾阃犳垚绋嫔簭链鑳介喷鏀惧凡缁忎笉鍐崭娇鐢ㄧ殑鍐呭瓨镄勬儏鍐点傚唴瀛樻硠婕忓苟闱炴寚鍐呭瓨鍦ㄧ墿鐞嗕笂镄勬秷澶憋纴钥屾槸搴旂敤绋嫔簭鍒嗛厤镆愭靛唴瀛桦悗锛岀敱浜庤捐¢敊璇锛屽け铡讳简瀵硅ユ靛唴瀛樼殑鎺у埗锛屽洜钥岄犳垚浜嗗唴瀛樼殑娴璐广
鍙浠ヤ娇鐢ㄧ浉搴旂殑杞浠舵祴璇曞伐鍏峰硅蒋浠惰繘琛屾娴嬨
1. ccmalloc钬斺擫inux鍜孲olaris涓嫔笴鍜孋++绋嫔簭镄勭亩鍗旷殑浣跨敤鍐呭瓨娉勬纺鍜宫alloc璋冭瘯搴撱
2. Dmalloc钬斺挤ebug Malloc Library.
3. Electric
Fence钬斺擫inux鍒嗗彂鐗堜腑鐢盉ruce Perens缂栧啓镄刴alloc锛堬级璋冭瘯搴撱
4. Leaky钬斺擫inux涓嬫娴嫔唴瀛樻硠婕忕殑绋嫔簭銆
5. LeakTracer钬斺擫inux銆丼olaris鍜孒P-UX涓嬭窡韪鍜屽垎鏋怌++绋嫔簭涓镄勫唴瀛樻硠婕忋
6. MEMWATCH钬斺旂敱Johan
Lindh缂栧啓锛屾槸涓涓寮鏀炬簮浠g爜C璇瑷鍐呭瓨阌栾妫娴嫔伐鍏凤纴涓昏佹槸阃氲繃gcc镄刾recessor𨱒ヨ繘琛屻
7. Valgrind钬斺挤ebugging and profiling Linux programs, aiming at
programs written in C and C++.
8. KCachegrind钬斺擜 visualization tool for the profiling data
generated by Cachegrind and Calltree.
9. Leak
Monitor钬斺斾竴涓狥irefox镓╁𪾢锛岃兘镓惧嚭璺烣irefox鐩稿叧镄勬硠婕忕被鍨嬨
10. IE Leak Detector
锛图rip/IE Sieve锛夆斺挤rip鍜孖E Sieve leak
detectors甯锷╃绣椤靛紑鍙戝憳鎻愬崌锷ㄦ佺绣椤垫ц兘阃氲繃鎶ュ憡鍙阆垮厤镄勫洜涓篒E灞闄愮殑鍐呭瓨娉勬纺銆
11. Windows Leaks
Detector钬斺旀帰娴嬩换浣昗in32搴旂敤绋嫔簭涓镄勪换浣曡祫婧愭硠婕忥纸鍐呭瓨锛屽彞镆勭瓑锛夛纴锘轰簬Win API璋幂敤阍╁瓙銆
12. SAP Memory
Analyzer钬斺旀槸涓娆惧紑婧愮殑java鍐呭瓨鍒嗘瀽杞浠讹纴鍙鐢ㄤ簬杈呭姪镆ユ垒JAVA绋嫔簭镄勫唴瀛樻硠婕忥纴鑳藉规槗镓惧埌澶у潡鍐呭瓨骞堕獙璇佽皝鍦ㄤ竴鐩村崰鐢ㄥ畠锛屽畠鏄锘轰簬Eclipse
RCP锛圧ich Client Platform锛夛纴鍙浠ヤ笅杞絉CP镄勭嫭绔嬬増链鎴栬匛clipse镄勬彃浠躲
13. DTrace钬斺斿嵆锷ㄦ佽窡韪狣ynamic
Tracing锛屾槸涓娆惧紑婧愯蒋浠讹纴鑳藉湪Unix绫讳技骞冲彴杩愯岋纴鐢ㄦ埛鑳藉熷姩镐佹娴嬫搷浣灭郴缁熷唴镙稿拰鐢ㄦ埛杩涚▼锛屼互镟寸簿纭鍦版帉鎻$郴缁熺殑璧勬簮浣跨敤鐘跺喌锛屾彁楂樼郴缁熸ц兘锛屽噺灏戞敮鎸佹垚链锛屽苟杩涜屾湁鏁堢殑璋冭妭銆
14. IBM Rational PurifyPlus钬斺斿府锷╁紑鍙戜汉锻樻煡鏄嶤/C++銆佹墭绠°侼ET銆丣ava鍜孷B6浠g爜涓镄勬ц兘鍜屽彲闱犳ч敊璇銆侾urifyPlus
灏嗗唴瀛橀敊璇鍜屾硠婕忔娴嬨佸簲鐢ㄧ▼搴忔ц兘鎻忚堪銆佷唬镰佽嗙洊鍒嗘瀽绛夊姛鑳界粍钖埚湪涓涓鍗曚竴銆佸畬鏁寸殑宸ュ叿鍖呬腑銆
15. Parasoft Insure++钬斺旈拡瀵笴/C++搴旂敤镄勮繍琛屾椂阌栾镊锷ㄦ娴嫔伐鍏凤纴瀹冭兘澶熻嚜锷ㄧ洃娴婥/C++绋嫔簭锛屽彂鐜板叾涓瀛桦湪镌镄勫唴瀛樼牬鍧忋佸唴瀛樻硠婕忋佹寚阍堥敊璇鍜孖/O绛夐敊璇銆傚苟阃氲繃浣跨敤涓绯诲垪镫鐗圭殑鎶链锛圫CI鎶链鍜屽彉寮傛祴璇旷瓑锛夛纴褰诲簳镄勬镆ュ拰娴嬭瘯鎴戜滑镄勪唬镰侊纴绮剧‘瀹氢綅阌栾镄勫嗳纭浣岖疆骞剁粰鍑鸿︾粏镄勮瘖鏂淇℃伅銆傝兘浣滀负Microsoft
Visual C++镄勪竴涓鎻掍欢杩愯屻
16. Compuware DevPartner for Visual C++ BoundsChecker
Suite钬斺斾负C++寮鍙戣呰捐$殑杩愯岄敊璇妫娴嫔拰璋冭瘯宸ュ叿杞浠躲备綔涓篗icrosoft Visual Studio鍜孋++ 6.0镄勪竴涓鎻掍欢杩愯屻
17. Electric Software GlowCode钬斺斿寘𨰾鍐呭瓨娉勬纺妫镆ワ纴code
profiler锛屽嚱鏁拌皟鐢ㄨ窡韪绛夊姛鑳姐傜粰C++鍜屻侼et寮鍙戣呮彁渚涘畬鏁寸殑阌栾璇婃柇锛屽拰杩愯屾椂镐ц兘鍒嗘瀽宸ュ叿鍖呫
18. Compuware DevPartner Java
Edition钬斺斿寘钖猕ava鍐呭瓨妫娴嬶纴浠g爜瑕嗙洊鐜囨祴璇曪纴浠g爜镐ц兘娴嬭瘯锛岀嚎绋嬫婚挛锛屽垎甯冨纺搴旂敤绛夊嚑澶у姛鑳芥ā鍧椼
19. Quest JProbe钬斺斿垎鏋怞ava镄勫唴瀛樻硠婕忋
20. ej-technologies JProfiler钬斺斾竴涓鍏ㄥ姛鑳界殑Java鍓栨瀽宸ュ叿锛屼笓鐢ㄤ簬鍒嗘瀽J2SE鍜孞2EE搴旂敤绋嫔簭銆傚畠鎶奀PU銆佹墽琛岀华鍜屽唴瀛樼殑鍓栨瀽缁勫悎鍦ㄤ竴涓寮哄ぇ镄勫簲鐢ㄤ腑銆侸Profiler鍙鎻愪緵璁稿欼DE鏁村悎鍜屽簲鐢ㄦ湇锷″櫒鏁村悎鐢ㄩ斻侸Profiler鐩磋夊纺镄凣UI璁╀綘鍙浠ユ垒鍒版晥鑳界摱棰堛佹姄鍑哄唴瀛樻硠婕忋佸苟瑙e喅镓ц岀华镄勯梾棰樸4.3.2娉ㄥ唽镰侊细A-G666#76114F-1olm9mv1i5uuly#0126
21. BEA JRockit钬斺旂敤𨱒ヨ瘖鏂璊ava鍐呭瓨娉勬纺骞舵寚鍑烘牴链铡熷洜锛屼笓闂ㄩ拡瀵笽ntel骞冲彴骞跺缑鍒颁紭鍖栵纴鑳藉湪Intel纭浠朵笂銮峰缑链楂樼殑镐ц兘銆
22. SciTech Software AB .NET Memory
Profiler钬斺旀垒鍒板唴瀛樻硠婕忓苟浼桦寲鍐呭瓨浣跨敤阍埚笴#锛孷B.Net锛屾垨鍏跺畠銆侼et绋嫔簭銆
23. YourKit .NET & Java Profiler钬斺斾笟鐣岄嗗厛镄凧ava鍜屻侼ET绋嫔簭镐ц兘鍒嗘瀽宸ュ叿銆
24. AutomatedQA AQTime钬斺擜utomatedQA镄勮幏濂栦骇鍝乸erformance profiling鍜宫emory
debugging宸ュ叿闆嗙殑涓嬩竴浠f浛鎹浜у搧锛屾敮鎸丮icrosoft, Borland, Intel, Compaq 鍜
GNU缂栬疟鍣ㄣ傚彲浠ヤ负銆侼ET鍜学indows绋嫔簭鐢熸垚鍏ㄩ溃缁呜嚧镄勬姤锻婏纴浠庤屽府锷╂偍杞绘涧闅旂诲苟鎺挜櫎浠g爜涓钖链夌殑镐ц兘闂棰桦拰鍐呭瓨/璧勬簮娉勯湶闂棰樸傛敮鎸併侼et
1.0,1.1,2.0,3.0鍜学indows 32/64浣嶅簲鐢ㄧ▼搴忋
25. JavaScript Memory Leak Detector钬斺斿井杞鍏ㄧ悆浜у搧寮鍙戞ф床锲㈤槦锛圙lobal Proct
Development- Europe team, GPDE锛
鍙戝竷镄勪竴娆捐皟璇曞伐鍏凤纴鐢ㄦ潵鎺㈡祴JavaScript浠g爜涓镄勫唴瀛樻硠婕忥纴杩愯屼负IE绯诲垪镄勪竴涓鎻掍欢銆
‘叁’ 如何定位java内存泄露
1、为什么会发生内存泄漏
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
其中深蓝色的部分就为内存泄漏的部分,java的堆内存一共只有481.5M而内存泄漏的部分独自占有了336.2M所以本次的内存泄漏很明显,那么我就来看看那个方法导致的内存泄漏:
从上图我们可以发现红线圈着的方法占用了堆内存的67.75%,如果能把这个测试结果交给开发,开发是不是应该很好定位呢。所以作为一名高级测试工程师,我们需要学习的东西太多。
虽然不确定一定是内存泄漏,但是可以准确的告诉开发问题出现的原因,有一定的说服力。
‘肆’ 记一次服务Full GC背后的内存泄漏问题,真是匪夷所思
最近所负责的服务略频繁地收到4xx告警
1、查业务日志,没发现相关错误的日志
2、查nginx access log,发现返回的状态码都是499,从request_uri查了一遍发现不是集中在某一个请求上,说明应该不是某个接口的问题了,有可能进程层面问题了。
通过对upstream_addr 分类,可以看到问题基本都是集中在 某一台这台机器上
3、网上资料了解到,499 是 nginx 扩展的 4xx 错误,代表客户端请求还未返回时,客户端主动断开连接。原因有几种,不过大部分原因都说到有可能服务器upstream处理过慢,导致用户提前关闭连接。那就先往这个方向排查,登录机器查看实际的access.log
发现upstream response都是10s以上。这就证明了上游服务器处理10秒还没有响应,因此nginx提前关闭链接,返回499
4、为什么进程响应如此慢,10秒太不正常了。考虑到那段时间就只有一台机器有问题,而且是进程层面的问题,首先想到的是GC,于是再次登录到机器上查看gc log。发现有Full GC,时间点和告警的时间也吻合。 惊呆的是,这次FullGC耗时长达19.07秒,由于我们的服务使用的是jdk8默认的ParallelGC,FullGC期间,整个应用Stop The World的。这是非常恐怖的一件事
由此看来,4xx告警的初步原因已经定位到,就是FullGC导致的。
那么究竟为什么会发生FullGC呢?需要深入分析一下。
借助服务治理平台的JVM监控观察了几天。期间不同机器不同时间也发生了几次FullGC。从监控发现,基本每台机器隔两天就会发生一次FullGC,每次FullGC后年老代回收的垃圾不算多,使用比例还是挺高的。
为什么年老代空间占用这么多?
继续分析上面那条full gc log,
1、发生full gc时,年老代内存已经占用了99.98%了(1048397/1048576)。看起来因为年老代满了而触发的FullGC了。
2、full gc回收了年老代大约302M的垃圾,回收后年老代占用70.4%(738282/1048576)。这占用率还是比较高的。
1、首先jmap简单打印一下所有对象的信息。发现有ClassPathList和ClassClassPath两个类的对象数量高达1000多万,并且这两个数量是一样的。仿佛嗅到了内存泄漏的味道。
2、只依靠对象统计信息,不足以定位问题,需要使用完整HeapDump,通过MAT进一步分析
jmap把完整堆heapDump下来
隔一段时间后,继续jmap,这次只取存活对象的mp(实际效果是先执行一次FULL GC)
可以看到,经过Full GC后,ClassPathList对象没有被回收,数量反而继续增加。到这里,基本可以确定,ClassPathList是存在泄漏了。
那么,ClassPathList究竟被谁引用着,导致回收不掉呢?
通过MAT的OQL过滤出老生代的ClassPathList对象,从对象的关联关系上继续深入分析。
首先需要知道老生代的地址区间,可以使用vjtools
通过vjmap的address命令,快速打印各代地址。
可以得知,oldGen的下界是0x80000000,上界是0xc0000000(注意OQL中使用时要把数值前的那串0去掉)。
执行OQL只查询年老代中的ClassPathList对象:执行OQL只查询年老代中的ClassPathList对象:
抽取其中一个对象分析,可以发现这个ClassPathList对象被一连串不同ClassPathList对象的next属性引用着。看起来是个链表的结构
再看看GCRoot,发现是被AppClassLoader也就是我们的应用类加载器引用着。除非这个加载器卸载了,否则ClassPathList对象是不会被GC掉了。
分析到这里,似乎离真相越来越近了。到底这个ClassPathList在项目中哪里使用到了?
通过前面的分析知道了ClassPathList的整体引用关系链:
AppClassLoader -> ClassPool类的defaultPool字段 -> ClassPoolTail类的source字段 -> ClassPathList类的pathList
可以看到,ClassPathList有两个属性,一个是next,结合之前MAT的分析,ClassPathList的确就是一个链表的结构。随着时间的增长,ClassPathList不断新增,链表也随之变得越来越大,最后内存占用逐渐上升。
另一个path字段属于ClassPath类型,ClassPath是个接口,查看它的实现类,发现一个似曾相识的名称ClassClassPath,之前分析对象统计信息时,还有一个类的对象数量是和ClassPathList一样的,正是这个ClassClassPath。每新增一个ClassPathList,都会伴随着新增对应的ClassPath对象,这也解释了为什么两者数量是一致的了。
通过注释知道,这个ClassClassPath的作用大概就是,利用一个叫ClassPool的对象,可以调用其insertClassPath方法来新增一个ClassClassPath对象,insertClassPath方法内部通过头插法将ClassClassPath添加到ClassPathList链表,从而形成一个search-path,然后通过这个search-path能够获取到某一个Class类的信息。
于是尝试着搜了一下,看看项目中有没有调用到insertClassPath方法的地方。意外发现一个类,
这不就是我们项目用来打印方法入参、执行耗时、上报metrics的@AutoLog的实现类吗。
可以看到getParams方法中调用了insertClassPath,注解@AutoLog的printParams默认为true,也就是每次调用都需要打印方法入参,每次打印前都要调用getParams先获取参数名称。因此每次都会insertClassPath,从而导致ClassPathList链表越来越大。
至此,内存泄漏的元兇已经找到。解决方法也就简单了。
因为目标只是想得到方法的参数名称,通过JoinPoint其实能直接获取到,因此可以改成JoinPoint获取的方式。
为了进行对比,分别在修改前后各进行一次压测。压测JVM参数大致与线上一致,为了尽快看到效果,只是调小了heap的大小。-Xms200m -Xmx200m
ClassPathList数量不断增长
年老代每次能回收的垃圾越来越少,每次回收过后的剩余空间也越来越小。最终整个年老代被撑满
虽然还没触发OOM,但是CPU负载飙高,从基本都在处于频繁的FULLGC状态
ClassPathList已经被消灭掉了
FullGC也趋于规律化了。每次回收的垃圾大致都相同
第一种方式是在启动参数增加 -XX:+PrintHeapAtGC,每次GC都打印地址
第二种方式是使用vjmap的命令,在-old, -sur, -address 中,都会打印出该区间的地址
第三种方式,使用vjmap的address命令,快速打印各代地址,不会造成过长时间停顿。
附: 我们服务的JVM参数
‘伍’ 内存溢出和内存泄漏的区别,产生原因以及解决方案
内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。
内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。
memory leak会最终会导致out of memory!
内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出。
内存泄漏是指你向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),结果你申请到的那块内存你自己也不能再访问(也许你把它的地址给弄丢了),而系统也不能再次将它分配给需要的程序。一个盘子用尽各种方法只能装4个果子,你装了5个,结果掉倒地上不能吃了。这就是溢出!比方说栈,栈满时再做进栈必定产生空间溢出,叫上溢,栈空时再做退栈也产生空间溢出,称为下溢。就是分配的内存不足以放下数据项序列,称为内存溢出.
以发生的方式来分类,内存泄漏可以分为4类:
1. 常发性内存泄漏。发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。
2. 偶发性内存泄漏。发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。
3. 一次性内存泄漏。发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏。比如,在类的构造函数中分配内存,在析构函数中却没有释放该内存,所以内存泄漏只会发生一次。
4. 隐式内存泄漏。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。
从用户使用程序的角度来看,内存泄漏本身不会产生什么危害,作为一般的用户,根本感觉不到内存泄漏的存在。真正有危害的是内存泄漏的堆积,这会最终消耗尽系统所有的内存。从这个角度来说,一次性内存泄漏并没有什么危害,因为它不会堆积,而隐式内存泄漏危害性则非常大,因为较之于常发性和偶发性内存泄漏它更难被检测到
内存溢出的原因以及解决方法
引起内存溢出的原因有很多种,小编列举一下常见的有以下几种:
1.内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
2.集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
3.代码中存在死循环或循环产生过多重复的对象实体;
4.使用的第三方软件中的BUG;
5.启动参数内存值设定的过小
内存溢出的解决方案:
第一步,修改JVM启动参数,直接增加内存。(-Xms,-Xmx参数一定不要忘记加。)
第二步,检查错误日志,查看“OutOfMemory”错误前是否有其它异常或错误。
第三步,对代码进行走查和分析,找出可能发生内存溢出的位置。
重点排查以下几点:
1.检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。
2.检查代码中是否有死循环或递归调用。
3.检查是否有大循环重复产生新对象实体。
4.检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。
5.检查List、MAP等集合对象是否有使用完后,未清除的问题。List、MAP等集合对象会始终存有对对象的引用,使得这些对象不能被GC回收。
第四步,使用内存查看工具动态查看内存使用情况