一、浅析
内存泄露( memory leak):是指程序在申请内存后,无法释放已申请的内存空间,多次内存泄露堆积后果很严重,内存迟早会被占光。内存泄漏最终会造成内存溢出。
内存溢出(out of memory) :是指程序在申请内存时,没有足够的内存空间供其使用
JVM中有一下几种内存空间:
- 栈内存(Stack):每个线程私有的。
- 堆内存(Heap):所有线程公用的。
- 方法区(Method Area):有点像以前常说的“进程代码段”,这里面存放了每个加载类的反射信息、类函数的代码、编译时常量等信息。
- 原生方法栈(Native Method Stack):主要用于JNI中的原生代码,平时很少涉及。
Java的内存回收机制:
Java堆是一个运行时数据区,类的实例(对象)从中分配空间,JVM堆中储存着正在运行的应用程序所建立的所有对象,“垃圾回收”主要也是和堆有关。
不论哪种语言的内存分配方式,都需要返回所分配内存的真实地址,也就是返回一个指针到内存块的首地址,Java中对象是采用new或者反射的方法创建的本文来源gao*daima.com搞@代#码&网6,这些对象的创建都是在堆(Heap)中分配的。
二、Java内存泄露
内存泄露是指当前未被引用的对象持续占用内存导致内存空间的浪费。常见的内存泄漏有以下几大类:
(1)静态集合类引起
比如说静态HashMap、Vector等,这些静态变量的生命周期和应用程序一致,他们所引用的所有的对象Object也不能被释放。
Static Vector v = new Vector(10); for (int i = 1; i<100; i++) { Object o = new Object(); v.add(o); o = null; }//
如上所示,循环申请Object 对象,并将所申请的对象放入一个Vector 中,如果仅仅释放引用本身(o=null),那么Vector 仍然引用该对象,所以这个对象对GC 来说是不可回收的。
必须要降Vector对象设置为null,才能回收这部分占用的内存
(2)当集合里面的对象属性被修改后,再调用remove()方法时不起作用。
主要原因是:set类存储对象是通过hashcode存储,如对象属性被修改,remove方法就不能通过原先的hashcode删除对象。
public static void main(String[] args) { Set<Person> set = new HashSet<Person>(); Person p1 = new Person("唐僧","pwd1",25); Person p2 = new Person("孙悟空","pwd2",26); Person p3 = new Person("猪八戒","pwd3",27); set.add(p1); set.add(p2); set.add(p3); System.out.println("总共有:"+set.size()+" 个元素!"); //结果:总共有:3 个元素! p3.setAge(2); //修改p3的年龄,此时p3元素对应的hashcode值发生改变,remove是通过hashcode删除对象 set.remove(p3); //此时remove不掉,造成内存泄漏 set.add(p3); //重新添加,居然添加成功 System.out.println("总共有:"+set.size()+" 个元素!"); //结果:总共有:4 个元素! for (Person person : set) { System.out.println(person); } }
(3)监听器
监听器调用太多,释放对象时未删除监听器也可能造成内存泄漏
(4)各种连接
数据库连接(dataSourse.getConnection()),网络连接(socket)和io连接,除非其显式的调用了其close()方法将其连接关闭,否则是不会自动被GC 回收的。Connection一旦回收,Resultset 和Statement 对象就会立即为NULL
如果使用连接池,Resultset 和Statement 对象也需要显式的关闭,否则就会造成大量的Statement 对象无法释放,从而引起内存泄漏,这种情况下一般都会在try里面去的连接,在finally里面释放连接。