重写hashCode()方法可能引起的内存溢出问题_JAVA_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > JAVA > 重写hashCode()方法可能引起的内存溢出问题

重写hashCode()方法可能引起的内存溢出问题

 2013/11/30 0:26:46  mabaoshan  程序员俱乐部  我要评论(0)
  • 摘要:我们在设计开发工程中,经常会用到HashMap,HashSet,HashTable等Java类,如果查看这些类的add,put等方法时,会发现这些方法都是基于参数对象的hashCode方法返回值经过一系列运算后,确定对象在哈希表中的位置。如果我们没有重写hashCode方法且所有父类也没有重写过Object的该方法,hashCode方法将使用Object类中native的hashCode方法,经验证,此方法产生的haseCode与子对象中的成员字段值无关系。下面通过一段代码
  • 标签:方法 has Hash 问题 内存

?

  我们在设计开发工程中,经常会用到HashMap,HashSet,HashTable等Java类,如果查看这些类的add,put等方法时,会发现这些方法都是基于参数对象的hashCode方法返回值经过一系列运算后,确定对象在哈希表中的位置。如果我们没有重写hashCode方法且所有父类也没有重写过Object的该方法,hashCode方法将使用Object类中native的hashCode方法,经验证,此方法产生的haseCode与子对象中的成员字段值无关系。

  下面通过一段代码,分析下如果重写hashCode方法,在使用不当时会怎样引起内存泄漏问题。

class="java" name="code">//代码1
public class HashSetOOM {
	public static void main(String[] args) {
		HashSet<MyClassA> set=new HashSet<MyClassA>();
		for(int i=0;i<1000;i++){//如果你的虚拟机最大堆设置的比较大,可以调整这里的循环次数,多加几个对象进去
			MyClassA a=new MyClassA();
			a.data=new byte[2048000];
			a.x=i;
			set.add(a);
			a.x=i+1;
			set.remove(a);
			a.hashCode();
		}
	}
}
/**
 * 我们定义一个类,该为了让该类的对象占用较大的空间,字段中使用一个byte[]数组。
 * 此处重写了hasecode方法,方法使用eclipse自动生成代码。
 * 同时重写equals方法,同样使用eclipse自动生成的代码
 */
class MyClassA{
	byte[] data;
	int x;
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + Arrays.hashCode(data);
		result = prime * result + x;
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		MyClassA other = (MyClassA) obj;
		if (!Arrays.equals(data, other.data))
			return false;
		if (x != other.x)
			return false;
		return true;
	}
}

?  通常,运行上边代码1,都会出现OOM情况。虽然,我们在循环的时候调用了set的remove方法。但是,事实情况下对象并没有从set中删除掉。我们查看add和remove方法时,会发现查找对象所在hash表中的位置时,是使用了这样一条逻辑:对象的“hashcode&tab.length-1”,操作是实际是表中该位置上的对象。如果,我们按上述代码修改后,hashcode将会发生改变,此时在进行remove的时候,可能将会出现两种情况:

  1、由于找不到此hashcode对应的index,remove不做任何操作。

  2、从hashset中删除了一个与之修改后生成对象的hashcode相等的对象。例如有这样一个类:

?

class Persion{
	String name;//姓名
	int age;//年龄
	int weight;//体重
	public Persion(String name,int age,int weight){
		this.name=name;
		this.age=age;
		this.weight=weight;
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		result = prime * result + weight;
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Persion other = (Persion) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		if (weight != other.weight)
			return false;
		return true;
	}
}
?  我们定义了如下两个对象

?

Person person1=new?Person("张三",60,70);//一个年龄为60岁,体重为70公斤的张三

Person person2=new?Person("张三",71,60);//一个年龄为71岁,体重为60公斤的张三

将这两个对象都add到hashset中,后发现,person2的年龄错了,要修改成70岁,于是乎我们按上边的代码执行时,person1被从hashset中remove掉了,只是我们不想看到的。

?

  经过查阅资料及求助于各大搜索引擎发现,这种问题在设计层面竟然没有好的办法可以规避。出问题的点就在:重写hashcode方法后,修改成员字段数值会影响hashcode值,进而影响到对象在哈希表中的位置,致使remove方法无法找到原来的对象,但是,对象有在哈希表中有引用,hash对象又没有被释放。垃圾回收器发现其还有引用,又不会回收该对象。于是,内存泄漏了。

  总结:重写hashcode方法的类对象,在放入hash表后,一定要谨慎操作对象字段属性值。如果要修改,先从哈希表中删除该对象,修改后再放入哈希表。

  

?

?

上一篇: 重写equals时,需要重写hashcode 下一篇: 没有下一篇了!
发表评论
用户名: 匿名