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

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© straw 中级黑马   /  2013-9-19 15:35  /  12346 人查看  /  6 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

这个问题不记得是谁提起过,那时没想通是什么原因,这段时间在摸索java对内存对象的管理实现才知道.
1,在阐述这个问题之前首先要了解JVM的垃圾回收实现原理,这个功能是java内存对象管理者的其中一个线程,用于释放内存中不在被使用的对像,其实现过程是依次遍历对象的索引将没有被其他对象应用的对象释放掉.也就是java的垃圾回收只能释放那些没有被引用的对象空间,有很多程序在编写的时候运行很正常,但部署到服务器不久后就有内存溢出.
2,那么集合在程序中造成内存溢出有两种原因:
  (1),集合装载的数据量过大,超出了JVM执行的内存范围,这个时候就有内存溢出.通常解决办法是分段读取数据量,减少内存在短时间内的开销,或者加大设 置JVM内存(默认是64M).
  (2),集合在程序中被大循环调用,不断在内存中创建无法被垃圾回收机制回收的集合对象,当程序运行到一定时期时集合对象就会沾满内存,造成内存溢出.
比如以下代码:

  1. <P><FONT style="BACKGROUND-COLOR: #ffffff">public class Demo {
  2. //定义一个静态变量
  3. static Integer i=0;
  4. public static void main(String[] ages){
  5.   //无线循环调用test()方法
  6.   while(true){
  7.    new Demo().test();
  8.   }
  9. }
  10. //定义一个被循环调用的方法
  11. public void test(){
  12.   i++;//i值一直在变
  13.   //定义一个HashMap集合
  14.   HashMap hashMap=new HashMap();
  15.   /*
  16.   *在此hashMap集合并没有跟随该方法的运行结束而被释放.因为集合装载有一个静态常量
  17.   *也就是集合中的元素被其他对象引用,所以垃圾回收机制不能回收hashMap对象
  18.   *而在方法每次执行时都创建一个新的集合去装载一个新的静态元素"i".
  19.   *所以集合会被无限的循环创建对象存在内存中知道内存溢出
  20.   *
  21.   */
  22.   hashMap.put(null, i);
  23. }
  24. }</FONT></P>
复制代码
针对第二个问题,目前也有很多解决方案,我个人总结了下,大致有三种:
(1),不要往集合中存入静态元素或者其他生命周期比集合生命周期还要长的元素.以至于使得GC能够回收释放.
(2),如果在大型的程序中,不能确确保集合要存入其他静态变量或者使集合不能在方法运行结束后跟着释放的元素,那么在集合被使用完成后调用clear()方法移除集合中所有元素,以便之后垃圾回收机制能回收释放.
(3),如果在程序中集合要被不断循环创建使用,那么就将该集合写成成员静态身份.防止在循环中不断创建出新的集合实例,同时也减少了程序在循环中不再去创建新集合实例时而消耗的资源.

这是本人一点皮毛的见解,希望路过的大虾别吐槽...




6 个回复

倒序浏览
楼主研究很深啊,表示敬佩,坐个沙发
回复 使用道具 举报
第一印象 好快!
回复 使用道具 举报
import java.util.HashMap;
import java.util.Map;

public class HashMapOver {

        public static void main(String[] args) {
                Map<Person, Integer> map = new HashMap<Person, Integer>();
                Person p = new Person("zhangsan", 12);

                map.put(p, 1);
                p.setName("lisi"); // 因为p.name参与了hash值的计算,修改了之后hash值发生了变化,所以下面删除不掉
                map.remove(p);

                System.out.println(map.size());
        }

}

class Person {
        private String name;
        private int age;

        public Person(String name, int age) {
                super();
                this.name = name;
                this.age = age;
        }

        public String getName() {
                return name;
        }

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

        public int getAge() {
                return age;
        }

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

        @Override
        public boolean equals(Object obj) {
                // TODO Auto-generated method stub
                return super.equals(obj);
        }

        @Override
        public int hashCode() {
                return name.hashCode() * 123;
        }
}

刚敲的代码,这样貌似也会造成内存泄漏吧、、、本意是删除map里面的东西,却没有删除,hash值发生了变化!
回复 使用道具 举报
恩,这个情况不会造成内存泄露的.
1,首先不管是对象p还是改变hash值后的p和集合map在该方法执行完成后都会随着被释放掉.因为这几个对象没有被生命周期比该方法生命周期还要长的其他对象引用.所以会跟着方法的结束而结束.这些对象都没常驻在内存
2,你在仔细检查代码,我测试出来的hash只是没有改变的哦.不变的原因我认为是因为javaBean或者应用类型对象的hash值只是根据对象的内存地址来计算的,每次执行程序的时候地址都不一样所以hash值也不一样,而且同一个对象不会因为对象内某属性值的改变而改变其hash值.对于int等基本数据类型的hash值时根据数据的真实值来计算的,而跟对象的内存地址没有关系.所以int类型变量的hash值总是不变的比如new Integer(1)对象的hash值始终是1.
这是我的一点见解希望对你有帮助,如果有哪里不对的话一定要题出来哦
回复 使用道具 举报
  Map<Person, Integer> map = new HashMap<Person, Integer>();
                Person p = new Person("zhangsan", 12);
                System.out.println(p.hashCode());

                map.put(p, 1);
                p.setName("lisi"); // 因为p.name参与了hash值的计算,修改了之后hash值发生了变化,所以下面删除不掉
                System.out.println(p.hashCode());
                map.remove(p);
               
                System.out.println(map.size());

下面是运行结果--->


-116701252
408606369
1

我运行结果的hash值改变了啊!删的是原来的hash值所指向的对象,但是hash值改变了,删不了,如果那里碰巧有其他数据的话,会不会删掉了不应该删掉的东西呢、、、
回复 使用道具 举报
straw 中级黑马 2013-9-25 03:07:19
7#
yting_xmei1129 发表于 2013-9-21 11:10
Map map = new HashMap();
                Person p = new Person("zhangsan", 12);
                Sy ...

原来你复写了hashCode()方法,用name属性的hashCode码参与了计算,难怪p对的hashCode码会变.没注意看下面的代码,还以为只是标准的bean.
当p.setName("lisi")时p已经变成另一个对象存在内存中,但这个新对象没有被保存在map集合里,所以在调用map.remove(p)时返回null,但是map集合里还保存着原先的p对象,所以这里的map集合始终还是长度1.
下面是我做的几个有趣的测试,你看看:
  1. public static void main(String[] ages){
  2. Person p=new Person("zhangsan",12);
  3. HashMap<Person,Integer> m=new HashMap<Person,Integer>();
  4. m.put(p, 1);
  5. p.setName("lisi");
  6. m.put(p, 2);//将改变后的新对象p添加到m集合中,成功添加说明p已经是一个新的对象
  7. System.out.println(m.size());//这时长度为2
  8. System.out.println("删除了:"+m.remove(p));//删除m集合中对应key的映射值,如果成则返回被删除的映射对应,如果删除失败则返回null,这里输出2,说明被删除的是新对象.
  9. System.out.println(m.size());//m集合长度已经变成1了

  10. }
复制代码
输出:
2
删除了:2
1



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