hashmap 不释放空间_【Java实践】HashMap使用不当导致内存泄漏问题

0. 背景

HashMap 是 Java中非常常用的一个集合类,它是一个Map接口的数据结构的实现。它的性能很好,查询对象只需要O(1)时间复杂度。

尽管HashMap使用非常方便,但是如果使用不当,可能会导致内存泄漏的。以下章节将介绍 HashMap可能导致内存泄漏的情况,以及如何解决该问题。

1. HashMap 的存储

首先,我们先明确HashMap的内部是如何存储数据的。

HashMap是一个用于存储Key-Value 对结构的集合类。Key可以是任意一个Java 的对象,甚至是可以是 null。

那么,HashMap的内部是怎么存储KV对的呢?很简单,就是用数组存放的。内部定义了一个 Node 类表示每一个 KV对,然后使用一个数组来存放所有的 Node 对象。

每新增一个KV对,就会用Key 的hashCode 计算出一个数组的下标index。然后用Key 和 Value创建一个Node对象,放在之前计算出的下标上。简言之,就是用一个数组存放数据,存放的位置是根据Key的hashCode计算出来的。这也就是它被称为HashMap 的原因。(实际的实现要相对复杂,这里只是大致的原理)

2. HashMap使用不当导致的内存泄露

在上一章节,我们也简单分析了HashMap的存储原理,从它的原理分析有一个很重要的信息:

HashMap中数据在数组中的存放位置,是取决于Key对象的 hashCode() 方法的。

这个信息原本是没什么问题,可以我们做java 的都知道,一个对象的hashCode() 方法的值,一般来说都是和对象的内容相关的。那么,如果Key对象的成员取值变化了,它的hashCode() 基本上也会变化。

这样就会有问题了。

在存放进去的时候,和取出数值的时候,都是依赖Key 对象的hashCode计算的。

而 hashCode 的计算,则是由类中定义的,根据hashCode的定义规范,重写hashCode方法之后,如果类的成员数值变化了,那么它的hashCode基本上也是会变化的。

设想一下,一个KV对被put 到HashMap中的时候,hashCode 是A,然后这个时候修改了Key的内容,使得它的hashCode变化了;那么接下来再用 get 方法获取这个 Value 时,会用这个Key重新计算hashCode,然后去内部查找,由于hashCode已经变化了,所以这时是找不到的。下面看一个例子:

/*** 定义一个类*/

class Point{

int x;

int y;

@Override

public int hashCode() {

// 重写hashCode实现。随便写的一个hashCode实现 return 17 + x * 23 + y +100;

}

}

public static void main (String[] args){

Point p1 = new Point();

p1.x = 100;

p1.y = 100;

Map map = new HashMap<>();

map.put(p1, "Point p1");

// 修改p1的内容,会导致hashCode 变化 p1.x = 50;

// result 是null String result = map.get(p1);

}

从以上例子中,可以看到,一个Point 类对象被当作Key放进了HashMap,但是后来却获取不到了。如果在外部已经没有对 p1 变量的引用的话,那么这个在HashMap内部存在的它曾经对应的Node,将会导致内存泄漏。它会因为HashMap的持有而无法被GC回收,但是用HashMap却也获取不到它。

总结一下,按照下面的场景使用HashMap的话,会出现内存泄漏的问题:

1. 作为Key的类有自定义的 hashCode() 方法,并且该方法的返回值受Key的类成员影响;

2. 在一个Key被put 到HashMap中以后,它的内部成员值发生了变化,并且导致了 hashCode 的结果变化;

3. 如何解决该问题?

从以上的分析可以知道,该问题的本质原因,在一个Key被放进HashMap 和从 HashMap 取出时,两次计算它的 hashCode 不一致导致的。所以要避免这个问题,就需要保证一个 Key 在 put 和 get 时,它的hashCode 保持不变。

具体实现为:

1. 保证作为HashMap 的Key 的对象是不可变的。也就是使用只读多对象来当作Key;

2. 如果要使用一个类当作HashMap的key,同时也要修改它的内容。那么可以重写hashCode,保证hashCode 的生成只和某些不可变的成员相关。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值