内存泄露(memory leak) VS 内存溢出(out of memory)
内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;
内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间(指的是堆上的内存),一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。
memory leak会最终会导致out of memory!
GC为了能够正确释放对象,会监控每个对象的运行状况,对他们的申请、引用、被引用、赋值等状况进行监控,Java会使用有向图的方法进行管理内存,实时监控对象是否可以达到,如果不可到达,则就将其回收。
内存泄露可以通过完善代码来避免;内存溢出可以通过调整配置来减少发生频率,但无法彻底避免。
Java内存泄露根本原因:
长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收,这就是java中内存泄露的发生场景。具体主要有如下几大类:
静态变量是全局的,GC不会回收。
1、静态集合类引起内存泄露:像HashMap、Vector等的使用最容易出现内存泄露,这些静态变量的生命周期和应用程序一致,他们所引用的所有的对象Object也不能被释放,因为他们也将一直被Vector等引用着。
如果仅仅释放引用本身(o=null,表示o这个引用不在指向堆中的内存,但是vector这个引用没有释放,一直在引用原来o指向的内存),那么Vector 仍然引用该对象,所以这个对象对GC 来说是不可回收的。因此,如果对象加入到Vector 后,还必须从Vector 中删除,最简单的方法就是将Vector对象设置为null。
public class MemoryLeakTest {
public static void main(String[] args) {
Vector v = new Vector(10);
for (int i = 1; i<100; i++)
{
Object o = new Object();
v.add(o);
o = null;
}//
}
}
2、 当集合里面的对象属性被修改后,再调用remove()方法时可能不起作用。
考虑hanshMap中,key值如果变化,再remove是remote不掉了,就会导致内存泄漏。
3、监听器
在java 编程中,我们都需要和监听器打交道,通常一个应用当中会用到很多监听器,我们会调用一个控件的诸如addXXXListener()等方法来增加监听器,但往往在释放对象的时候却没有记住去删除这些监听器,从而增加了内存泄漏的机会。
4、各种连接
比如数据库连接(dataSourse.getConnection()),网络连接(socket)和io连接,除非其显式的调用了其close()方法将其连接关闭,否则是不会自动被GC 回收的。对于Resultset 和Statement 对象可以不进行显式回收,但Connection 一定要显式回收,因为Connection 在任何时候都无法自动回收,而Connection一旦回收,Resultset 和Statement 对象就会立即为NULL。但是如果使用连接池,情况就不一样了,除了要显式地关闭连接,还必须显式地关闭Resultset Statement 对象(关闭其中一个,另外一个也会关闭),否则就会造成大量的Statement 对象无法释放,从而引起内存泄漏。这种情况下一般都会在try里面去的连接,在finally里面释放连接。
5、如果单例对象持有外部对象的引用,那么这个外部对象将不能被jvm正常回收,导致内存泄露
不正确使用单例模式是引起内存泄露的一个常见问题,单例对象在被初始化后将在JVM的整个生命周期中存在(以静态变量的方式),如果单例对象持有外部对象的引用,那么这个外部对象将不能被jvm正常回收,导致内存泄露,考虑下面的例子:
那么如何避免内存泄漏:
- 尽早释放无用对象的引用:使用临时变量的时候,让引用变量在退出活动域后自动设置为null,暗示垃圾收集器来收集该对象,防止发生内存泄露。
- 进行字符串处理时,尽量避免使用String,而应使用StringBuffer。
- 尽量少用静态变量
- 避免集中创建对象尤其是大对象,如果可以的话尽量使用流操作
- 尽量运用对象池技术以提高系统性能。
- 不要在经常调用的方法中创建对象,尤其是忌讳在循环中创建对象
内存溢出的解决方案:
- 是从代码层面进行优化完善,尽量避免该情况发生;
- 二是调整优化服务器配置:
具体内容参看其他相关话题(http://www.cnblogs.com/liufei1983/p/8996186.html)
二:java内存管理
Java的内存管理就是对象的分配和释放问题。在Java中,内存 的分配是由程序完成的,而内存的释放是由垃圾收集器(GarbageCollection,GC)完成的,程序员不需要通过调用函数来释放内存,但它只能 回收无用并且不再被其它对象引用的那些对象所占用的空间。
Java的内存垃圾回收机制是从程序的主要运行对象开始检查引用链,当遍历一遍后发现没有被引用的孤立对象就作为垃圾回收。GC为了能够正确释放对 象,必须监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等,GC都需要进行监控。监视对象状态是为了更加准确地、及时地释放对象,而释放 对象的根本原则就是该对象不再被引用。
比如在函数中,String s = new String("abc"); s是一个栈中的引用,指向堆中的对象, 当函数调用完后s这个引用就没有了,堆中的内存就会被释放。
在Java中,这些无用的对象都由GC负责回收,因此程序员不需要考虑这部分的内存泄露。虽然,我们有几个函数可以访问GC,例如运行GC的函数 System.gc(),但是根据Java语言规范定义,该函数不保证JVM的垃圾收集器一定会执行。因为不同的JVM实现者可能使用不同的算法管理 GC。通常GC的线程的优先级别较低。JVM调用GC的策略也有很多种,有的是内存使用到达一定程度时,GC才开始工作,也有定时执行的,有的是平缓执行 GC,有的是中断式执行GC。但通常来说,我们不需要关心这些。
二 :如果出现内存溢出,如何调整虚拟参数
JVM初始分配的内存由-Xms指定,默认是物理内存的1/64;
JVM最大分配的内存由-Xmx指定,默认是物理内存的1/4。
JVM内存的最大值跟操作系统有很大的关系。32位处理器虽然可控内存空间有4GB,但是具体的操作系统会给一个限制,这个限制一般是2GB-3GB(一般来说Windows系统下为1.5G-2G,Linux系统下为2G-3G),而64bit以上的处理器就不会有限制了。
三: 如何检测内存泄漏, 分析DUMP文件
可以通过一些性能监测分析工具,如 JProfiler、Optimizeit Profiler。