纳尼,Java不是自动管理内存吗?怎么可能会出现内存泄泄泄泄泄泄漏!
Java最牛逼的一个特性就是垃圾回收机制,不用像c++需要手动管理内存,所以作为Java程序员很幸福,只管新新新即可,反正Java会自动回收过期的对象…
那么Java都自动管理内存了,那怎么会出现内存泄漏,难道Jvm有错误# 63;不要急,且听我慢慢道来. .
<强>怎么判断可以被回收强>
先了解一下Jvm是怎么判断一个对象可以被回收。一般有两种方式,一种是引用计数法,一种是可达性分析。
<强>引用计数法强>:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。
这个办法看起来挺简单的,但是如果出现一个引用了B, B又引用了,这时候就算他们都不再使用了,但因为相互引用计算器=1永远无法被回收。
此方法简单,无法解决对象相互循环引用的问题。
<>强可达性分析(可达性分析):强>从GC根开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC根没有任何引用链相连时,则证明此对象是不可用的,那么虚拟机就判断是可回收对象。
可达性分析可以解决循环引用的问题。
那么gc根对象是哪些呢
-
<李>虚拟机栈中引用的对象李>
<李>方法区中类静态属性引用的对象李>
<李>方法区中常量引用的对象李>
<李>本地方法栈中JNI(即一般说的原生]引用的对象李>
目前主流的虚拟机中大多使用可达性分析的方式来判定对象是否可被GC回收。
<强>什么情况下会出现内存泄漏强>
既然可达性分析好像已经很牛逼的样子了,怎么可能还会出现内存泄漏呢,那我们再来看一下内存泄漏的定义。
<强>内存泄露就是指一个不再被程序使用的对象或变量一直被占据在内存中。强>
有可能此对象已经不使用了,但是还有其它对象保持着此对象的引用,就会导致GC不能回收此对象,这种情况下就会出现内存泄漏。
写一个程序让出现内存泄漏
①长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收。
公开课简单{ 对象对象; 公共空间method1 () { 对象=新对象();//渌? } }
这里的对象实例,其实我们期望它只作用于method1()方法中,且其他地方不会再用到它,但是,当method1()方法执行完成后,对象对象所分配的内存不会马上被认为是可以被释放的对象,只有在简单类创建的对象被释放后才会被释放,严格的说,这就是一种内存泄露。
解决方法就是将对象作为method1()方法中的局部变量。
公开课简单{ 对象对象; 公共空间method1 () { 对象=新对象();//渌? 对象=零; } }
当然大家有可能会想就这一个方法也不会有多大影响,但如果在某些项目中,一个方法在一分钟之内调用上万次的时候,就会出现很明显的内存泄漏现象。
②集合中的内存泄漏,比如HashMap, ArrayList等,这些对象经常会发生内存泄露比。如当它们被声明为静态对象时,它们的生命周期会跟应用程序的生命周期一样长,很容易造成内存不足。
下面给出了一个关于集合内存泄露的例子。
向量v=新的向量(10); for (int i=1; i<100;我+ +) { 对象o=新对象(); v.add (o); o=零; }//此时,所有的对象对象都没有被释放,因为变量v引用这些对象。
在这个例子中,我们循环申请对象对象,并将所申请的对象放入一个向量中,如果我们仅仅释放引用本身,那么向量仍然引用该对象,所以这个对象对GC来说是不可回收的。
因此,如果对象加入到向量后,还必须从向量中删除,最简单的方法就是将矢量对象设置为零。
以上两种是最常见的内存泄漏案例。当然还有一些内存泄漏的例子,这里就不再一一例举了,感兴趣的同学可以在网上找找资料。