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.弊端:容易引起数据丢失,慎用!!!



作者:烨枫_邱
链接:https://www.jianshu.com/p/16eb452c6eb1
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

猜你喜欢

转载自blog.csdn.net/yangxiaobo118/article/details/79773288
今日推荐