关于 equals和hasCode,上一遍文章有说过了,http://www.luckybird.me/java-%E5%85%B3%E4%BA%8Ehascode%E5%92%8Cequals.html
下面说,equals和hasCode导致内存泄露的现象。
1,缺少equals和hasCode,默认hasCode采用内存地址,使得散列类型变量比如HashMap重复保存相同的对象,占用额外的内存。
比如下面代码,重复保存map.put(new Key(“dummyKey”), “value”),其实是相同的value
import java.util.HashMap; import java.util.Map; public class JavaTest { public static void main(String[] args) { Mapmap = new HashMap (1000); int counter = 0; while (true) { // creates duplicate objects due to bad Key class for(int i = 100;i>0;i--){ map.put(new Key("dummyKey"), "value"); } counter++; if (counter % 1000 == 0) { System.out.println("map size: " + map.size()); System.out.println("Free memory after count " + counter + " is " + getFreeMemory() + "MB"); sleep(1000); } } } // inner class key without hashcode() or equals() -- bad implementation static class Key { private String key; public Key(String key) { this.key = key; } } //delay for a given period in milli seconds public static void sleep(long sleepFor) { try { Thread.sleep(sleepFor); } catch (InterruptedException e) { e.printStackTrace(); } } //get available memory in MB public static long getFreeMemory() { return Runtime.getRuntime().freeMemory() / (1024 * 1024); } }
运行结果,内存不断被额外消耗,最终导致内存不足
map size: 100000 Free memory after count 1000 is 170MB map size: 200000 Free memory after count 2000 is 158MB map size: 300000 Free memory after count 3000 is 151MB map size: 400000 Free memory after count 4000 is 137MB map size: 500000 Free memory after count 5000 is 130MB map size: 600000 Free memory after count 6000 is 123MB map size: 700000 Free memory after count 7000 is 116MB map size: 800000 Free memory after count 8000 is 93MB map size: 900000 Free memory after count 9000 is 87MB
2,通过remove销毁散列类型变量中的对象不成功,无用对象依然占用内存,导致内存泄露
remove对象时会根据hasCode去查找,如果hasCode不存在,就不会接着进行删除对象了。
如果hasCode是根据对象属性创建的,当对象属性改变时,那么hasCode也会更新,这么remove时就找不到原来的对象了,即remove失败,原对象还占用内存
import java.util.HashMap; import java.util.HashSet; import java.util.Map; public class JavaTest { public static void main(String[] args){ HashSetset = new HashSet (); RectObject r1 = new RectObject(3,3); RectObject r2 = new RectObject(4,4); RectObject r3 = new RectObject(5,5); set.add(r1); set.add(r2); set.add(r3); System.out.println("before edit"); System.out.println("before edit r1.hashCode:"+r1.hashCode()); /* for(RectObject i : set){ System.out.println("set:"+i+",x,"+i.getX()+",y,"+i.getY()); }*/ r1.setX(6); System.out.println("after edit"); System.out.println("after edit r1.hashCode:"+r1.hashCode()); /* for(RectObject i : set){ System.out.println("set:"+i+",x,"+i.getX()+",y,"+i.getY()); }*/ System.out.println("before remove set.size:"+set.size()); set.remove(r1); System.out.println("after remove set.size:"+set.size()); System.out.println("after remove r1.hashCode:"+r1.hashCode()); /* for(RectObject i : set){ System.out.println("set:"+i+",x,"+i.getX()+",y,"+i.getY()); }*/ } } class RectObject { public int x; public int y; public RectObject(int x,int y){ this.x = x; this.y = y; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } @Override public int hashCode(){ final int prime = 31; int result = 1; result = prime * result + x; result = prime * result + y; return result; } @Override public boolean equals(Object obj){ if(this == obj) return true; if(obj == null) return false; if(getClass() != obj.getClass()) return false; final RectObject other = (RectObject)obj; if(x != other.x){ return false; } if(y != other.y){ return false; } return true; } }
运行效果如下:
before edit before edit r1.hashCode:1057 after edit after edit r1.hashCode:1150 before remove set.size:3 after remove set.size:3 after remove r1.hashCode:1150
Leave a Reply