如果GC无法回收内存中不再使用的对象,则定义为内存有泄露
未关闭的资源类
当我们在程序中打开一个新的流或者是新建一个网络连接的时候,JVM都会为这些资源类分配内存做缓存,常见的资源类有网络连接,数据库连接以及IO流。值得注意的是,如果在业务处理中异常,则有可能导致程序不能执行关闭资源类的代码,因此最好按照下面的做法处理资源类
- public void handleResource() {
- try {
- // open connection
- // handle business
- } catch (Throwable t) {
- // log stack
- } finally {
- // close connection
- }
- }
未正确实现equals()和hashCode()
假如有下面的这个类
- public class Person {
- public String name;
- public Person(String name) {
- this.name = name;
- }
- }
并且如果在程序中有下面的操作
- @Test
- public void givenMapWhenEqualsAndHashCodeNotOverriddenThenMemoryLeak() {
- Map<Person, Integer> map = new HashMap<>();
- for(int i=0; i<100; i++) {
- map.put(new Person("jon"), 1);
- }
- Assert.assertFalse(map.size() == 1);
- }
可以预见,这个单元测试并不能通过,原因是Person类没有实现equals方法,因此使用Object的equals方法,直接比较实体对象的地址,所以map.size() == 100
如果我们改写Person类的代码如下所示:
- public class Person {
- public String name;
- public Person(String name) {
- this.name = name;
- }
- @Override
- public boolean equals(Object o) {
- if (o == this) return true;
- if (!(o instanceof Person)) {
- return false;
- }
- Person person = (Person) o;
- return person.name.equals(name);
- }
- @Override
- public int hashCode() {
- int result = 17;
- result = 31 * result + name.hashCode();
- return result;
- }
- }
则上文中的单元测试就可以顺利通过了,需要注意的是这个场景比较隐蔽,一定要在平时的代码中注意。
非静态内部类
要知道,所有的非静态类别类都持有外部类的引用,因此某些情况如果引用内部类可能延长外部类的生命周期,甚至持续到进程结束都不能回收外部类的空间,这类内存溢出一般在Android程序中比较多,只要MyAsyncTask处于运行状态MainActivity的内存就释放不了,很多时候安卓开发者这样做只是为了在内部类中拿到外部类的属性,殊不知,此时内存已经泄露了。