Linux下Java项目占用内存问题的分析
这两天发现一个问题,即自动化测试平台所在的服务器内存突然就爆了。从现象上看,服务器上执行命令行pandning,日志回吐卡顿,机器卡死......个人感觉这个问题比较蹊跷也很隐蔽,从原则上,讲为了不要眉毛胡子一把抓导致下错定义,故而花了些时间仔细研究了一下。
“内存一直占满”
首先,可以通过free 或者 free -b -s2查看一个Linux内存使用情况,如下图:
内存使用样例图当发现内存使用率这个数字一直攀升或者已经达到很大值时,就开始紧张起来了;别着急,接下来咱们先看看资料:
在Linux中经常发现空闲内存很少,似乎所有的内存都被系统占用了,表面感觉是内存不够用了,其实不然。这是Linux内存管理的一个优秀特性,在这方 面,区别于 Windows的内存管理。主要特点是,无论物理内存有多大,Linux 都将其充份利用,将一些程序调用过的硬盘数据读入内存,利用内存读写的高速特性来提高Linux系统的数据访问性能。而Windows 是只在需要内存时,才为应用程序分配内存,并不能充分利用大容量的内存空间。换句话说,每增加一些物理内存,Linux 都将能充分利用起来,发挥了硬件投资带来的好处,而Windows只将其做为摆设,即使增加8GB甚至更大。
Linux 的这一特性,主要是利用空闲的物理内存,划分出一部份空间,做为 cache 和 buffers ,以此提高数据访问性能。
Linux 优先使用物理内存,当物理内存还有空闲时,linux是不会施放内存的,即时占用内存的程序已经被关闭了(这部分内存就用来做缓存了)。也就是说,即时你有8G的内存,用过一段时间后,也会被占满。这样做的好处是,启动那些刚开启过的程序、或是读取刚存取过得数据会比较快,对于服务器很有好处。
你可以用 free 指令查看一下输出,用 used 减去 buffer 和 cache,才是你运行中的程序所占用的空间,举例如下:
total used free shared buffers cached
Mem: 8061672 7442680 618992 192 163748 5833736
-/+ buffers/cache: 1445196 6616476
Swap: 8191996 32540 8159456
7442680 - 163748 - 5833736 = 1445196
也就是说目前正在被使用的内存只有 1.3 G。不要被 7000多兆(合约7个G)的used吓住了。
windows则总是给内存留下一定的空闲空间,即时内存有空闲也会让程序使用一些虚拟内存,这样做的好处是,启动新的程序比较快,直接分给它些空闲内存就可以了,而linux下呢?由于内存经常处于全部被使用的状态,则要先清理出一块内存,再分配给新的程序使用,因此,新程序的启动会慢一些。
另外,内存是随机访问的,也就是说,无论你的内存占用了多少,数据的存取时间都是相同的,跟硬盘不同。
判断Java程序对内存的消耗
1.使用top
2.ps aux|head -1;ps aux|grep -v PID|sort -rn -k +3|head
具体查看%MEM即可
导致内存溢出的主要原因
内存溢出是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于虚拟机能提供的最大内存。(可以从以下几个方面去思考)
引起内存溢出的原因有很多种,常见的有以下几种:
1.内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
2.集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
3.代码中存在死循环或循环产生过多重复的对象实体;
4.使用的第三方软件中的BUG;
5.启动参数内存值设定的过小;
解决办法
内存溢出的解决方案:
第一步,修改JVM启动参数,直接增加内存。(-Xms,-Xmx参数一定不要忘记加。)
第二步,检查错误日志,查看“OutOfMemory”错误前是否有其它异常或错误。
第三步,对代码进行走查和分析,找出可能发生内存溢出的位置。
重点排查以下几点:
1.检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。
2.检查代码中是否有死循环或递归调用。
3.检查是否有大循环重复产生新对象实体。
4.检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中 数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。
5.检查List、MAP等集合对象是否有使用完后,未清除的问题。List、MAP等集合对象会始终存有对对象的引用,使得这些对象不能被GC回收。
6.做好监控,持续发现内存使用率的攀升(内存查看工具有许多,比较有名的有:Optimizeit Profiler、JProbe Profiler、JinSight和Java1.5的Jconsole等)
手动清理内存
1.free -m
2.开始清理
echo 1 > /proc/sys/vm/drop_caches
3.清理后内存使用情况
free -m
4.弊端:容易引起数据丢失,慎用!!!