`
mabaoshan
  • 浏览: 6839 次
  • 性别: Icon_minigender_1
  • 来自: 北京
最近访客 更多访客>>
社区版块
存档分类
最新评论

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

阅读更多

 

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

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

//代码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表后,一定要谨慎操作对象字段属性值。如果要修改,先从哈希表中删除该对象,修改后再放入哈希表。

  

 

 

分享到:
评论

相关推荐

    hashCode内存溢出和内存泄漏的问题解决.docx

    内存管理和优化是编程中至关重要的环节,尤其是在Java这样的高级语言中,由于自动内存管理机制的存在,内存泄漏和溢出问题可能会变得难以察觉但后果严重。本文将深入探讨内存泄漏及其可能导致的内存溢出问题,以及...

    C# Equals 和 GetHashCode 方法重写

    ### C# Equals 和 GetHashCode 方法重写 在C#编程中,`Equals` 和 `GetHashCode` 方法是非常重要的成员方法,它们对于确保对象的正确比较以及高效地存储和检索对象至关重要。这两个方法通常需要在自定义类中进行...

    面试基础总结.docx

    如果对象需要放入散列容器(如HashSet或HashMap),则还需重写hashcode()方法,以确保根据内容相等的对象具有相同的哈希码。equals()相等的对象,其hashcode()必须相等,但反之不成立,因为哈希冲突可能导致不同的...

    Java hashCode() 方法详细解读

    Java中的`hashCode()`方法是基于对象的内部状态计算出的一个整数值,它在处理...在实现自定义类时,正确地重写`hashCode()`和`equals()`方法可以确保对象在哈希表中的行为符合预期,并且能有效地利用哈希表的性能优势。

    Java.Bug模式详解

    当重写equals()方法时,务必同时重写hashCode()方法,以保持对象比较的一致性。否则,这可能导致HashSet或HashMap的行为异常。 九、资源未释放 数据库连接、网络连接等资源使用后应及时关闭。使用finally块或try-...

    Java 覆盖equals时总要覆盖hashcode

    如果没有重写`hashCode()`,当一个`PhoneNumber`对象作为键放入`HashMap`时,即使`equals()`方法返回`true`表示两个电话号码相等,由于它们的`hashCode()`可能不同,`HashMap`可能会将它们视为不同的键,导致预期的...

    Java[1].Bug模式详解.rar

    4. **内存泄漏**:未正确释放对象引用,导致内存无法回收,长期积累可能导致内存溢出。合理使用` WeakReference`和`SoftReference`,及时清理不再使用的对象,可以帮助防止内存泄漏。 5. **异常处理不当**:忽略...

    2022最新Java面试题及答案总结

    内存溢出通常由于内存分配过多或无法回收导致,常见类型有堆溢出、栈溢出和 PermGen(方法区)溢出。 20. **JVM类加载机制**: 类加载分为加载、验证、准备、解析和初始化五个阶段,确保类正确加载并准备执行。 ...

    java-专业】最全-JAVA面试题

    这只是Java面试中一小部分可能遇到的问题,面试中还可能涉及多线程、集合框架、IO流、异常处理、设计模式、反射、JVM内存模型、垃圾回收机制、数据库连接、网络编程、Spring框架等相关知识。全面掌握这些概念和实践...

    面试Java程序员最可能遇到的问题考试大全

    本文将详细解答在Java面试中可能会遇到的一些核心问题,这些问题涵盖了Java语言的基础概念、类库使用、异常处理、内存管理、多线程、设计模式等多个方面。以下是针对这些面试问题的详细解释: 1. **面向对象的特征*...

    有趣的BUG之Stack Overflow.doc

    然而,这里出现了一个问题:类A和类B之间存在循环依赖,并且两者都重写了`toString()`方法。 类A中包含一个B类型的属性,并在`toString()`方法中调用了`b.toString()`。同样,类B也有一个A类型的属性,并在其`...

    Java性能优化的45个细节

    6. **使用equals()与hashCode()的约定**:当重写equals()时,确保同时重写hashCode(),以保持散列码的一致性。 7. **避免在循环中使用同步方法或锁**:这会导致线程阻塞,影响性能。可以考虑使用并发容器,如...

    经典的Java面试题及回答集锦(基础篇)

    - 自定义对象作为key时,必须重写equals()和hashCode()方法,以确保对象比较逻辑的一致性,并且key对象应是不可变的,以防止后续修改导致的查找问题。 9. **线程启动与控制** - **启动线程**:使用`start()`,`...

    Java面试题 .docx

    - 使用`sun.misc.Unsafe`或`ByteBuffer`可以分配大内存,但需注意释放,否则可能导致内存泄漏。 20. **JVM配置和调优**: - 常见参数如-Xms、Xmx设置堆大小,-XX:NewRatio调整新生代与老年代比例,-XX:+UseG1GC...

    java面试题集锦.docx

    - 理解内存泄漏和内存溢出的问题,以及如何通过JVM参数进行调优。 以上只是Java面试中可能会遇到的部分主题,实际面试中还会涉及其他高级话题,如JVM原理、Spring框架、数据库操作、分布式系统等。为了在面试中...

    JAVA程序员面试笔试常见问题总结

    17. Error 通常表示系统级问题,如内存溢出,无法恢复;Exception 表示程序中的可预期错误,可以捕获处理。 18. List, Set, Map 分别继承自 Collection 接口的子接口:List 继承自 List 接口,Set 继承自 Set 接口...

    JAVA面试题,Java是一种面向对象的编程语言,由Sun Microsystems公司(现属Oracle公司)于1995年推出

    Java中的内存溢出通常由于以下几个原因造成: PermGen Space不足(存放类信息的空间),可以通过调整JVM参数解决;Java堆内存过载,导致过多对象创建,可以通过调整堆大小限制来防止;线程创建过多,超出系统资源...

    史上最全java面试题总结.pdf

    - **JVM内存管理**:包括堆内存(Heap)、栈内存(Stack)、方法区(Method Area)等区域,理解它们的工作原理有助于解决内存溢出等问题。 - **GC回收机制**:Java的垃圾收集器负责回收不再使用的对象,有多种算法...

    JavaWeb 面试点 列表(未附答案)

    重写equals通常需要同时重写hashCode,以保证哈希表的正确性。 17. **Java序列化**:序列化将对象转换为字节流,便于存储或网络传输;反序列化反之。Serializable接口标记可序列化的类。 18. **Object类方法**:...

Global site tag (gtag.js) - Google Analytics