今天面试了一家公司的java开发方面的实习生,被问到一个问题:如何处理java中的内存泄露问题,保证java的虚拟机内存不会被爆掉,当时其实觉得面试官的问题有点泛,所以也没有很好领会他的意思,答案也不是很准确,后来回去查了下资料,大概明白面试官要问的东西是什么(尴尬,才反应过来),然后,也特地简单总结下java内存溢出的相关内容,以备之后复习。
一、什么情况下会java内存泄露?
java不是有GC吗?为毛还会内存泄露?之前我也一直以为,java有垃圾回收器在,估计内存泄露的情况一般不会发生吧?后来发现并非如此,先看以下代码:
//内存泄露的简单例子 //首先,要明白,GC它回收的是不可到达的对象,但是,在static 的集合类中,引用可以到达,但是却有可能对象已经不用了 //首先定义一个静态变量 public static ArrayList<Object> list = new ArrayList<Object>(); public void stackOverExam(Object object){ //当非静态变量被static变量持有引用的时候,容易发生内存泄露,因为object是一直被list引用着的 list.add(object); object = null;//这里设置为null并没有达到释放object引用对应对象的效果,毕竟list还是持有引用 }
通过上面的代码,估计大家也可以看到:由于static指向的对象是不能被垃圾回收器回收的,所以,间接地object也无法回收,当业务对象很大而且很多时,便有内存泄露的风险了,所以我们可以总结到如下规则:当全局的静态变量持有局部变量(或者说,大范围的变量持有小范围变量而且小范围变量消耗内存较大、数目较多时),程序便有内存泄露的风险,一般来说,类似的例子还有,单例模式中的对象,模块之间的调用,内部类的引用被持有等
二、如何解决?
经过百度一番,其实发觉解决的思路大概和下面这方面的知识有关:java的对象引用有以下几类:强引用,软引用,弱引用。具体看下图
解决刚说的内存泄露问题,主要运用的就是显式声明引用的类型,首先简单解释下相关的知识:
1、引用的类型:强,软,弱。普通的java引用,也是我们最一般的java引用对象,便是强引用,它直接影响垃圾回收的进行(有强引用指向对应的对象,GC便不可回收该对象,即使内存不够用);软引用,和强引用的主要区别是:它会影响GC对对象的回收工作,但是,当虚拟机的内存不够用时,软引用所指向对象(当然,不能同时有强引用指向对象)会被强制回收;弱引用就等级更低,它无法影响GC对对象的垃圾回收工作,只能引用并访问对象。具体三种引用可以由下面的代码例子说明:
//软引用和弱引用的使用例子 public void referenceExam(){ Object object = new Object();//这个普通object引用就是一个强引用 //软引用通过SoftReference创建一个软引用 SoftReference<Object> softObjReference = new SoftReference<Object>(object); //通过get方法获取真正的对象引用 softObjReference.get(); //弱引用通过WeakReference来关联一个对象 WeakReference<Object> weakObjReference = new WeakReference<Object>(object); weakObjReference.get();//这个方法返回了真实的对象引用,当没有强引用或者弱引用指向它时,返回为null }
2、对于防止内存泄露以及与垃圾回收有关的引用强弱类型,我们可以归纳出下面的特点:
A、当我们在高速缓存中缓存较大的对象例如缓存图片等以提高读取速度的时候(针对大内存对象问题),可以将相关的对象设置为软引用对象,这样保证在内存不够用的时候GC可以进行回收工作。
B、当我们完全不像由于当前对对象的引用而影响对象的其他模块原来设定的GC回收时期,我们可以采用弱引用(相当于只可访问数据而访问不会对GC对对象的回收造成影响)
C、当然,java还有一种叫做虚引用的引用类型变量,该变量其实现实中用得不多,主要用来跟踪对象GC过程,所以这里不详细讲。
最后,至于什么时候要设置对象为弱引用(或者软引用),个人觉得还是要根据具体的业务进行对象使用的判断,如果全局对象过大,有可能在程序中造成泄露问题,或者说我们不想在不清楚对象何时可以被回收(工厂通过接口提供对象给客户)时,可以采用软引用或者弱引用进行对象访问。
当然,其实内存泄露是个很大的主题,这里仅仅是讨论了一小部分内容而已,各位有更多的见解也欢迎交流!