内存泄漏和内存溢出

一.什么叫内存泄漏和内存溢出

1.内存泄漏

       一般我们常说的内存泄漏是指堆内存的泄漏。堆内存是指程序从堆中分配的,大小任意的(内存块的大小可以在程序运行期决定),使用完后必须显式释放的内存。应用程序一般使用malloc,calloc,realloc,new等函数从堆中分配到一块内存,使用完后,程序必须负责相应的调用free或delete释放该内存块,否则,这块内存就不能被再次使用,我们就说这块内存泄漏了。

2.内存溢出

        内存溢出是指应用系统中存在无法回收的内存或使用的内存过多,使得程序运行要用到的内存大于能提供的最大内存。此时程序就运行不了,系统会提示内存溢出,有时会自动关闭软件,重启电脑或者软件后释放掉一部分内存又可以正常运行该软件。

二.内存泄漏和内存溢出的区别

        泄漏就是内存申请后,用完没有释放,造成可用内存越来越少。溢出指用户实际的数据长度超过了申请的内存空间大小,导致覆盖了其他正常数据,容易造成程序异常,严重的,攻击者可以以此获取程序控制权。

三.场景

1.内存泄漏的场景

    1). 内存中数据量太大,比如一次性从数据库中取出来太多数据

    2). 静态集合类中对对象的引用,在使用完后未清空(只把对象设为null,而不是从集合中移除),使JVM不能回      收,即内存泄漏

    3). 静态方法中只能使用全局静态变量,而如果静态变量又持有静态方法传入的参数对象的引用,会引起内存       泄漏

    4). 代码中存在死循环,或者循环过多,产生过多的重复的对象

    5). JVM启动参数内存值设置过小

        a. 堆内存:JVM默认为64M,-Xms堆的最小值, -Xmx堆的最大值,OutOfMemoryError: java heap space

        b. 栈内存:-Xss,StackOverflowError,栈太深

        c. 永久代内存:-XX:PermSize,-XX:MaxPermSize,OutOfMemoryError: PermGen space,加载的类过多

    6). 监听器:addXXListener,没有remove

    7). 各种连接没有关闭:例如数据库连接、网络连接

    8). 单例模式:如果单例对象持有外部对象的引用,那么外部对象将不会被回收,引起内存泄漏

    9). 一个类含有静态变量,这个类的对象就无法被回收

    10). ThreadLocal

2.内存溢出的场景

    1).大量位图的加载

    2).位图对象没有及时释放

    3).查询数据库没有关闭游标

    4).构造 Adapter 时,没有使用缓存的 convertView

四.解决方法

1.内存溢出的解决方法

        第一步,就是修改 JVM 启动参数,直接增加内存。这一点看上去似乎很简单,但很容易被忽略。JVM 默认可以使用的内存为 64M,Tomcat 默认可以使用的内存为 128MB,对于稍复杂一点的系统就会不够用。在某项目中,就因为启动参数使用的默认值,经常报“Out Of Memory”错误。因此,-Xms,-Xmx 参数一定不要忘记加。

        第二步,检查错误日志,查看“Out Of Memory”错误前是否有其它异常或错误。在一个项目中,使用两个数据库连接,其中专用于发送短信的数据库连接使用 DBCP 连接池管理,用户为不将短信发出,有意将数据库连接用户名改错,使得日志中有许多数据库连接异常的日志,一段时间后,就出现“Out Of Memory”错误。经分析,这是由于 DBCP 连接池 BUG 引起的,数据库连接不上后,没有将连接释放,最终使得 DBCP 报“Out Of Memory”错误。经过修改正确数据库连接参数后,就没有再出现内存溢出的错误。查看日志对于分析内存溢出是非常重要的,通过仔细查看日志,分析内存溢出前做过哪些操作,可以大致定位有问题的模块。

        第三步,安排有经验的编程人员对代码进行走查和分析,找出可能发生内存溢出的位置。重点排查以下几点:检查代码中是否有死循环或递归调用。检查是否有大循环重复产生新对象实体。检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。检查 List、MAP 等集合对象是否有使用完后,未清除的问题。List、MAP 等集合对象会始终存有对对象的引用,使得这些对象不能被 GC 回收。

        第四步,使用内存查看工具动态查看内存使用情况。某个项目上线后,每次系统启动两天后,就会出现内存溢出的错误。这种情况一般是代码中出现了缓慢的内存泄漏,用上面三个步骤解决不了,这就需要使用内存查看工具了。内存查看工具有许多,比较有名的有:Optimizeit Profiler、JProbeProfiler、JinSight 和 Java1.5 的 Jconsole 等。它们的基本工作原理大同小异,都是监测 Java 程序运行时所有对象的申请、释放等动作,将内存管理的所有信息进行统计、分析、可视化。开发人员可以根据这些信息判断程序是否有内存泄漏问题。一般来说,一个正常的系统在其启动完成后其内存的占用量是基本稳定的,而不应该是无限制的增长的。持续地观察系统运行时使用的内存的大小,可以看到在内存使用监控窗口中是基本规则的锯齿形的图线,如果内存的大小持续地增长,则说明系统存在内存泄漏问题。通过间隔一段时间取一次内存快照,然后对内存快照中对象的使用与引用等信息进行比对与分析,可以找出是哪个类的对象在泄漏。

        通过以上四个步骤的分析与处理,基本能处理内存溢出的问题。当然,在这些过程中也需要相当的经验与敏感度,需要在实际的开发与调试过程中不断积累。

你可能感兴趣的:(内存泄漏和内存溢出)