内存溢出:简单地说内存溢出就是指程序运行过程当中申请的内存大于系统可以提供的内存,致使没法申请到足够的内存,因而就发生了内存溢出。java
内存泄漏:内存泄漏指程序运行过程当中分配内存给临时变量,用完以后却没有被GC回收,始终占用着内存,既不能被使用也不能被分配给其余程序,因而就发生了内存泄漏。算法
内存溢出有如下几种常见的状况:数据库
(1)java.lang.OutOfMemoryError: PermGen space (持久带溢出)网络
咱们知道jvm经过持久带实现了java虚拟机规范中的方法区,而运行时常量池就是保存在方法区中的,所以发生这种溢出多是运行时常量池溢出,或是因为程序中使用了大量的jar或class,使得方法区中保存的class对象没有被及时回收或者class信息占用的内存超过了配置的大小。jvm
(2) java.lang.OutOfMemoryError: Java heap space (堆溢出)socket
发生这种溢出的缘由通常是建立的对象太多,在进行垃圾回收以前对象数量达到了最大堆的容量限制。工具
解决这个区域异常的方法通常是经过内存映像分析工具对Dump出来的堆转储快照进行分析,看究竟是内存溢出仍是内存泄漏。若是是内存泄漏,可进一步经过工具查看泄漏对象到GC Roots的引用链,定位出泄漏代码的位置,修改程序或算法;若是不存在泄漏,就是说内存中的对象确实都还必须存活,那就应该检查虚拟机的堆参数-Xmx(最大堆大小)和-Xms(初始堆大小),与机器物理内存对比看是否能够调大。spa
(3)虚拟机栈和本地方法栈溢出线程
若是线程请求的栈深度大于虚拟机所容许的最大深度,将抛出StackOverflowErrorcode
若是虚拟机在扩展栈时没法申请到足够的内存空间,则抛出OutOfMemoryError
内存泄漏的根本缘由是长生命周期的对象持有短生命周期对象的引用,尽管短生命周期的对象已经再也不须要,但因为长生命周期对象持有它的引用而致使不能被回收。
下面总结几种常见的内存泄漏:
(1)静态集合类引发的内存泄漏
HashTable的使用最容易出现内存泄漏,如HashMap,Vector等,这些变量的生命周期和程序一致,它们所持有的对象不能被释放,从而形成内存泄漏。以下面的例子:
Vector<Object> v=new Vector<Object>(100); for (int i = 1; i<100; i++) { Object o = new Object(); v.add(o); o = null; }
循环申请Object对象,并将所申请的对象放入到一个Vector中,若是仅仅释放引用自己(o=null),那么Vector 仍然引用该对象,因此这个对象对GC 来讲是不可回收的。所以,若是对象加入到Vector 后,还必须从Vector 中删除,最简单的方法就是将Vector对象设置为null。
(2)修改HashSet中对象的参数值,且参数是计算哈希值的字段
当一个对象被存储到HashSet集合中之后,修改了这个对象中那些参与计算哈希值的字段后,这个对象的哈希值与最初存储在集合中的就不一样了,这种状况下,用contains方法在集合中检索对象是找不到的,这将会致使没法从HashSet中删除当前对象,形成内存泄漏,举例以下:
public static void main(String[] args) { Set<Person> set = new HashSet<Person>(); Person p1 = new Person("张三","1",25); Person p2 = new Person("李四","2",26); Person p3 = new Person("王五","3",27); set.add(p1); set.add(p2); set.add(p3); System.out.println("总共有:"+set.size()+" 个元素!"); //结果:总共有:3 个元素! p3.setAge(2); //修改p3的年龄,此时p3元素对应的hashcode值发生改变 set.remove(p3); //此时remove不掉,形成内存泄漏 set.add(p3); //从新添加,能够添加成功 System.out.println("总共有:"+set.size()+" 个元素!"); //结果:总共有:4 个元素! for (Person person : set) { System.out.println(person); } }
注:实现这个例子时注意重写Person类的hashCode方法。
(3)监听器的使用
一般一个应用当中会用到不少监听器,咱们会调用一个控件诸如addXXXListener()等方法来增长监听器,但每每在释放对象的时候却没有记住去删除这些监听器,从而增长了内存泄漏的机会。
(4)各类链接
好比数据库链接(dataSourse.getConnection()),网络链接(socket)和io链接,除非其显式的调用了其close()方法将其链接关闭,不然是不会自动被GC 回收的。对于Resultset 和Statement 对象能够不进行显式回收,但Connection 必定要显式回收,由于Connection 在任什么时候候都没法自动回收,而Connection一旦回收,Resultset 和Statement 对象就会当即为NULL。可是若是使用链接池,状况就不同了,除了要显式地关闭链接,还必须显式地关闭Resultset Statement 对象(关闭其中一个,另一个也会关闭),不然就会形成大量的Statement 对象没法释放,从而引发内存泄漏。这种状况下通常都会在try里面去链接,在finally里面释放链接。
(1)尽早释放无用对象的引用
(2)避免在循环中建立对象
(3)使用字符串处理时避免使用String,应使用StringBuffer
(4)尽可能少使用静态变量,由于静态变量存放在永久代,基本不参与垃圾回收
以上有错误或者须要补充的地方还请指点,谢谢