A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 杜鹏云 中级黑马   /  2012-8-24 16:27  /  2284 人查看  /  11 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

HashSet为何能够引起内存泄露,看了张老师的讲解还是有些不甚了解,
还有就是equals和hashcode方法.这两个方法。。和内存泄露又有什么必然关系??

评分

参与人数 1技术分 +1 收起 理由
张_涛 + 1

查看全部评分

11 个回复

倒序浏览
不知道你说的是不是那个改了hash值的那个例子,hashset是以hashcode值存的,当x参与hashcode值运算时,当程序运行中将这个x改变了,hash值也就改了,那就不可能再找到原来地址的值了,原来地址的值就被漏掉了。引起内存泄露。我的理解,错了请拍砖。

评分

参与人数 1技术分 +1 收起 理由
张_涛 + 1

查看全部评分

回复 使用道具 举报
黑马-李勇 发表于 2012-8-24 16:34
不知道你说的是不是那个改了hash值的那个例子,hashset是以hashcode值存的,当x参与hashcode值运算时,当程 ...

是这个意思。。能具体些么
回复 使用道具 举报
我只看了一遍,记不太清了。下面这个例子意思是程序不要改变参与运算的x,改变后就会有内存遗漏或泄露。
        public int hashCode()
        {        return name.hashCode()+age*x;}
           //例:name.hashcode=20   age.hashcode=30 x=34
           //return 的值是20+30*34=1040
但在后面的程序中你的x变了。如  x=40了。!!!
再调用hashcode找那个值就成了。20+30*40=1220
如这时调用remove   返回的hashcode值就成了name.hashCode()+age*x    ---->  20+30*40    ---->1220
删除的是hashcode值为1220地址的值。可原来的1040地址的值已经没有指针指向了。此程序也不能找到原来的1040地址的值了。1040就泄露了。
个人理解,可能有不对的地方。

                                                                                 
回复 使用道具 举报
老师说的好象是不要改变参与hashcode运算的对象的值。这个不太确定。但应该是这个意思。你再看下吧,等我再看到那的时候再确认下。应该是张老师基础加强里面的。
回复 使用道具 举报
黑马-李勇 发表于 2012-8-24 16:56
我只看了一遍,记不太清了。下面这个例子意思是程序不要改变参与运算的x,改变后就会有内存遗漏或泄露。
        pu ...

JVM不会回收这个没用的地址所代表的对象
么??
回复 使用道具 举报
这个应该回收吧,但你的程序就出现了问题。上面那个例子,你要删除员工档案hashcode=1040的员工,但你把hashcode=1220的员工给删除了,1040的员工漏掉了。这程序就出现问题了。少了两个人了。

点评

HashCode的设计目的就是为了避免HashSet成员重复,所以不会把hashcode=1220的员工删除了,因为就不存在这个新员工。1220是以前那个1040员工的新哈希值  发表于 2012-8-24 19:07
回复 使用道具 举报
黑马-李勇 发表于 2012-8-24 17:06
这个应该回收吧,但你的程序就出现了问题。上面那个例子,你要删除员工档案hashcode=1040的员工,但你把has ...

这个我感觉是引用错了内存中的地址,,但是这个和内存泄露有什么关系??
回复 使用道具 举报
  1. package com.itheima.test;

  2. import java.util.Collection;
  3. import java.util.HashSet;

  4. public class Test {

  5.         public static void main(String[] args) {
  6.                 Collection<Person> collections = new HashSet<Person>();
  7.                
  8.                 Person p1 = new Person("zhangsan", 23);
  9.                 Person p2 = new Person("lisi", 34);
  10.                
  11.                 collections.add(p1);
  12.                 collections.add(p2);
  13.                
  14.                 System.out.println(collections.size());
  15.                
  16.                 p1.setAge(32);//p1的hashCode值也随之改变了
  17.                
  18.                 /*        想要移除p1,但是由于哈希算法内部原理,到另一个区域,却找不到p1,没有移除掉,故collections中还有2个元素
  19.                  *         长此以往,collections中元素越来越多,内存泄漏
  20.                  *         因为并不是无名引用对象,JVM不会自动释放内存的
  21.                 */
  22.                 collections.remove(p1);
  23.                 System.out.println(collections.size());
  24.                 System.out.println(p1);
  25.                 System.out.println(collections);
  26.                
  27.                 /*p2就可以移除掉*/
  28.                 collections.remove(p2);
  29.                 System.out.println(collections.size());
  30.         }
  31. }

  32. class Person {
  33.        
  34.         String name;
  35.         int age;
  36.        
  37.         Person(String name, int age) {
  38.                 this.name = name;
  39.                 this.age = age;
  40.         }

  41.         public String getName() {
  42.                 return name;
  43.         }

  44.         public void setName(String name) {
  45.                 this.name = name;
  46.         }

  47.         public int getAge() {
  48.                 return age;
  49.         }

  50.         public void setAge(int age) {
  51.                 this.age = age;
  52.         }

  53.         @Override
  54.         public int hashCode() {
  55.                 final int prime = 31;
  56.                 int result = 1;
  57.                 result = prime * result + age;
  58.                 result = prime * result + ((name == null) ? 0 : name.hashCode());
  59.                 return result;
  60.         }

  61.         @Override
  62.         public boolean equals(Object obj) {
  63.                 if (this == obj)
  64.                         return true;
  65.                 if (obj == null)
  66.                         return false;
  67.                 if (getClass() != obj.getClass())
  68.                         return false;
  69.                 Person other = (Person) obj;
  70.                 if (age != other.age)
  71.                         return false;
  72.                 if (name == null) {
  73.                         if (other.name != null)
  74.                                 return false;
  75.                 } else if (!name.equals(other.name))
  76.                         return false;
  77.                 return true;
  78.         }

  79.         @Override
  80.         public String toString() {
  81.                 return "Person [name=" + name + ", age=" + age + "]";
  82.         }
  83.        
  84. }
复制代码

评分

参与人数 1技术分 +1 收起 理由
张_涛 + 1

查看全部评分

回复 使用道具 举报
代码太长了,我把main函数摘出来把:
  1. public class Test {

  2.         public static void main(String[] args) {
  3.                 Collection<Person> collections = new HashSet<Person>();
  4.                
  5.                 Person p1 = new Person("zhangsan", 23);
  6.                 Person p2 = new Person("lisi", 34);
  7.                
  8.                 collections.add(p1);
  9.                 collections.add(p2);
  10.                
  11.                 System.out.println(collections.size());
  12.                
  13.                 p1.setAge(32);//p1的hashCode值也随之改变了
  14.                
  15.                 /*        想要移除p1,但是由于哈希算法内部原理,到另一个区域,却找不到p1,没有移除掉,故collections中还有2个元素
  16.                  *         长此以往,collections中元素越来越多,内存泄漏
  17.                  *         因为并不是无名引用对象,JVM不会自动释放内存的
  18.                 */
  19.                 collections.remove(p1);
  20.                 System.out.println(collections.size());
  21.                 System.out.println(p1);
  22.                 System.out.println(collections);
  23.                
  24.                 /*p2就可以移除掉*/
  25.                 collections.remove(p2);
  26.                 System.out.println(collections.size());
  27.         }
  28. }
复制代码
回复 使用道具 举报
打印结果是:
  1. 2
  2. 2
  3. Person [name=zhangsan, age=32]
  4. [Person [name=zhangsan, age=32], Person [name=lisi, age=34]]
  5. 1
复制代码
回复 使用道具 举报
本帖最后由 黄敏 于 2012-8-24 19:23 编辑
杜鹏云 发表于 2012-8-24 17:14
这个我感觉是引用错了内存中的地址,,但是这个和内存泄露有什么关系?? ...

import java.util.HashSet;

public class HashSetTest
{
        public static void sop(Object obj)
        {
                System.out.println(obj);
        }
        public static void main(String[] args)
        {
                HashSet hs = new HashSet();
                Person p1 = new Person("a1",11);      
                Person p2 = new Person("a2",12);
                Person p3 = new Person("a3",13);
                Person p4 = new Person("a1",11);
               
                hs.add(p1);                //p1存进hs中的时候根据你覆写hashCode方法算出来的“name.hashCode()+age*37”值存到到集合中分配的对应的区域块(暂且认为是区域01)中,这时你的算出来的hashCode应该是name.hashCode()+11*37.
                hs.add(p2);
                hs.add(p3);
                //hs.add(p4);
               
                p1.setAge(15);        //你设置了pi的年龄是15,又根据算出来的值是name.hashCode()+15*37.
                hs.remove(p1);       //当你要在集合中移除p1的时候,还是根据hashCode的值去集合中查找与之对应的p1,但是刚刚你上面改变了p1的年龄,算出来的hashCode值肯定不一样了  这个集合就会根据你刚刚的值去内存中对应的区域块查,发现没有对应的pi就没删了,这时的区域01就在集合成垃圾了,即使你把改后的p1添加到集合中,存起来的也是不同的区域,你删除的p1还不是区域01,还是垃圾

假如你现在往集合里添加了对象很多,不断地重复刚才的添加,修改,删除操作,上述的垃圾就一直增加,内存空间就越来越少,导致内存溢出。就是张老师说的如果参加hashCode运算的变量在HashSet集合中添加了,就不要随意修改,希望能帮助你理解   
                System.out.println(hs.size());
        }
}
class Person
{
        private String name;
        public void setName(String name) {
                this.name = name;
        }

        public void setAge(int age) {
                this.age = age;
        }
        private int age;
        Person(String name,int age)
        {
                this.name = name;
                this.age = age;
        }
        
        public int hashCode()
        {
                return name.hashCode()+age*37;
        }

        public boolean equals(Object obj)
        {
                if(!(obj instanceof Person))
                        return false;
                Person p = (Person)obj;
                return this.name.equals(p.name) && this.age == p.age;
        }

}

评分

参与人数 1技术分 +1 收起 理由
田建 + 1

查看全部评分

回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马