非常多人有疑问, java 有非常好的垃圾回收机制, 怎么会有内存泄露? 事实上是有的, 那么何为内存泄露? 在 Java 中所谓内存泄露就是指在程序执行的过程中产生了一些对象, 当不须要这些对象时, 他们却没有被垃圾回收掉, 并且程序执行中非常难发现这个对象. 它始终占领着内存却没有发挥作用.
我举这样一个样例, 在现实开发中我们须要自己定义一个先进后出的栈集合, 代码例如以下:
- package cn.lmj.demo2;
- import java.util.ArrayList;
- import java.util.List;
- public class MyStack
- {
- private List list = new ArrayList();
- private int len = 0;
- public void put(T obj)
- {
- list.add(obj);
- len++;
- }
- public T pop()
- {
- int index = --len;
- return list.get(index);
- }
- }
这个代码看起来和执行起来都没问题, 可是, 这里有个非常隐晦的问题, 就是在 pop() 方法里面. 我们首先找到集合最后一个元素的下标. 然后依照下标从集合中取出, 可是这个对象真的从集合中移走了吗? 答案不是的, 也就是说你取出来的对象看似从栈中取出来了. 可是它却还存在于集合中占领着内存. 并且你非常难发现它, 这就产生了内存泄露, 正确的 pop() 方法应该是
- public T pop()
- {
- int index = --len;
- return list.remove(index); // 取出的同一时候删除集合中的元素
- }
在 java 中, 还有这样一个场景也会出现内存泄露问题, 并且也是非常隐晦的, 我们在用 Map 存一对键值型的数据时. 我们假设存进去了, 那么就不要改动 Map 的 key 值參与计算的 hashCode 方法和 equals 方法, 例如以下代码就有内存泄露问题:
- package cn.lmj.demo2;
- import java.util.HashMap;
- import java.util.Map;
- public class Demo03
- {
- public static void main(String[] args)
- {
- Map map = new HashMap();
- Person p1 = new Person("aaa");
- Person p2 = new Person("bbb");
- Person p3 = new Person("ccc");
- map.put(p1,"1");
- map.put(p2,"2");
- map.put(p3,"3");
- System.out.println(map.containsKey(new Person("aaa")));//true
- p1.setName("eee"); // 改变參与计算的 hashCode 和 equals 值
- System.out.println(map.containsKey(new Person("aaa")));//false
- }
- }
- class Person
- {
- private String name;
- public Person(String name)
- {
- super();
- this.name = name;
- }
- public Person()
- {
- super();
- }
- // 利用 name 属性生成 hashCode 和 equals 方法
- @Override
- public int hashCode()
- {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((name == null) ?
- 0 : name.hashCode());
- return result;
- }
- @Override
- public boolean equals(Object obj)
- {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- Person other = (Person) obj;
- if (name == null)
- {
- if (other.name != null)
- return false;
- }
- else if (!name.equals(other.name))
- return false;
- return true;
- }
- public String getName()
- {
- return name;
- }
- public void setName(String name)
- {
- this.name = name;
- }
- }
总结:
在 java 中是有内存泄露的情况. 那么我们在开发中怎样避免内存泄露? 除了上面两种情况的以外:
1, 最主要的建议是尽早释放无用对象的引用. 如:
- .....
- A a = new A().
- // 应用 a 对象
- a = null. // 当使用对象 a 之后主动将其设置为空
- ...
注: 假设 a 是方法的返回值, 不要做这种处理. 否则你从该方法中得到的返回值永远为空, 并且这种错误不易被发现, 排除
2, 尽量少用 finalize 函数. 它会加大 GC 的工作量.
3, 假设须要使用经经常使用到的图片. 能够使用 soft 应用类型.
它尽可能把图片保存在内存中
4, 注意集合数据类型, 包含数组, 树, 图, 链表等数据结构, 这些数据结构对 GC 来说. 回收更为复杂.
5, 尽量避免在类的默认构造器中创建, 初始化大量的对象, 防止在调用其自类的构造器时造成不必要的内存资源浪费
6, 尽量避免强制系统做垃圾内存的回收. 增长系统做垃圾回收的终于时间
7, 尽量避免显式申请数组空间
8, 尽量做远程方法调用类应用开发时使用瞬间值变量. 除非远程调用端须要获取该瞬间值变量的值.
9, 尽量在合适的场景下使用对象池技术以提高系统性能.
来源: http://www.bubuko.com/infodetail-2862914.html