什么对象需要回收
在Java堆中存放着Java程序中新建的对象,也就是我们开发过程中 new,newarray 创建的对象。但没有明确的代码去释放他们。垃圾回收机制就是释放这些不再被程序所使用的对象的过程。
为什么要使用垃圾收集器
在虚拟机的堆内存中,当某些对象失去引用,对于虚拟机内存而言就是”垃圾”,虚拟就就会回收这部分内存,以供后续的新对象使用。除了释放不在被引用的对象外,
在堆空间中,并不是所有的内存空间都是连续的,因为在程序运行过程中每次新建对象都会申请堆内存,并不能保证内存可以整齐的码放在你面前。要是申请不到大片连续的内存区域,还可能造成“内存不足”的假象。
引用计数法
引用计数法是垃圾回收的早期策略,在堆中的每个对象都有一个引用来计数,每当有一个地方引用到它时,计数器的值就会加一;引用失效时,计数器值减一,当计数器的值为0时,就认为该对象就是不被使用的,可以进行垃圾回收,但这种算法缺钱,就是很难解决对象之间相互循环引用的问题;
public static void main(String[] args) {
GcObject obj1 = new GcObject(); //Step 1
GcObject obj2 = new GcObject(); //Step 2
obj1.instance = obj2; //Step 3
obj2.instance = obj1; //Step 4
obj1 = null; //Step 5
obj2 = null; //Step 6
}
}
class GcObject{
public Object instance = null;
}
当程序进行到Step 1 时 obj1 的计数器=1;
当程序进行到Step 2 时 obj2 的计数器=1;
程序进行到Step 3 时 obj1 的计数器=2;
程序进行到Step 4 时 obj2 的计数器=2;
此时obj1,obj2的技术都为2;
当程序进行到Step 5 时 obj1 的计数器-1 结果为1;
当程序进行到Step 6 时 obj2 的计数器-1 结果为1;如下图
那么如果采用的引用计数算法的话 GcObject实例1和实例2的计数引用都不为0,两个实例所占内存不到释放,于是就产生了内存泄露。
根搜索法(可达性算法)
根搜索法是通过一系列称为”GC Root”的对象作为起点。以这些节点作为根节点向下搜索,搜索过程中的路径成为“”引用链“”,当某个对象到”GC Root”没有任何引用链时,也就是“根对象”不可达。就被认为是可以被回收的对象,
“”GC Root“”对象包括以下几种:
虚拟机栈中的引用的对象;
方法去中类静态引用的对象;
常量引用的对象;
如上图中,GC Root对object1,object2,object3,object4都是仍存活的对象,object5,object6,object7都是GC Root 触及不到的,因此都时要被回收打的内存。
对于垃圾收集器力来说,堆中每个对象都有三种状态,可触及的,可复活的,不可触及的。不可触及的也就是我们刚提的 GC Root引用链连接不到的对象。对于另外的两种,可触及打的,是指每个对象从他生命的开始起,只要程序至少保留一个可触及的引用,那么它就是可触及的,一旦程序释放了所有对该对象的引用,那么这个对象就成了可复活状态。
若满足以下条件则为可复活状态:
从根节点连接不到,但是可能在垃圾收集器执行某些终结方法时触及;**不仅仅是声明了finalize()方法的对象,而是所有的对象都要经过可复活状态**
先来说finalize()方法,如果类中声明了此方法,垃圾回收器(GC)会在释放这个实例占据的内存空间之前,执行这个方法如下
class Final(){
protected void finalize(){
System.out.println("Final obj die!"); //go die
}
}
根搜索算法中不可达对象在回收之前,要进行二次标记。
第一次标记时会进行一次筛选:筛选的条件是是否有必要执行finalize()方法。
当对象没有覆盖finalize()方法,或者finalize()被虚拟机调用过,则虚拟机认为没有必要执行finalize()方法。
如果这个对象有必要执行finalize(),则会放在一个队列里,以一个低优先级的线程进行执行finalize()方法进行二次标记,如果在finalize()方法中,对象重新回到引用链上(比如this赋值给其他引用链上的对象),则该对象不被回收,而移出该队列。
注意:finalize()方法只被调用一次,如果这个对象在GC时被调用过一次finalize()方法,则第二次GC的时候,就会被判断为没有必要执行finalize()而被直接回收。
另外finalize()能做的所有工作,都可以通过try-finally更好、更及时的解决。所以请忘掉finalize().
public class TestGc {
public static TestGc HOOK;
@Override
protected void finalize() throws Throwable {
super.finalize();
TestGc.HOOK = this;
System.out.println("finalize");
}
/*
输出结果
finalize
HOOK is alvie
HOOK is dead
*/
public static void main(String[] args) throws Exception {
HOOK = new TestGc();
HOOK = null;
System.gc();//第一次GC,符合有必要要执行finalize的条件
Thread.sleep(500);//Finalizer线程优先级较低,所以要等一会
if (HOOK != null) {
System.out.println("HOOK is alvie");
} else {
System.out.println("HOOK is dead");
}
HOOK = null;
System.gc();//第二次GC,因为已经执行过一次finalize,所以没有必要进行二次标记
Thread.sleep(1000);//Finalizer线程优先级较低,所以要等一会
if (HOOK != null) {
System.out.println("HOOK is alvie");
} else {
System.out.println("HOOK is dead");
}
}
}
无论是引用计数法,还是跟搜索法都是引用操作,那么就有可能产生垃圾问题,但是对于引用也需要有一些合理化的设计。在很多的时候并不是所有的对象都需要被我们一直使用,那么就需要对引用的问题做进一步的思考。从JDK1.2之后关于引用提出了四种类型的引用:
demo 引自http://blog.csdn.net/qq_34280276/article/details/52863626;
●强引用:也就是我们new出来的对象当内存不足的时候,JVM宁可出现OutOfMemory错误停止,也需要进行保存,并且不会将此空间回收(永远不会被GC回收);
public class Demo{
public static void main(String[]args){
Object obj=new Object();//强引用,默认
Object ref=obj;//引用传递
obj=null;//断开连接
System.gc();
System.out.println(ref);
}
}
●软引用:当内存不足的时候,进行对象的回收处理,往往用于高速缓存中;
public class Demo{
public static void main(String[]args){
Object obj=new Object();
SoftReference<Object> ref=new SoftReference<Object><obj>;//软引用
obj=null;//断开连接
System.gc();
System.out.println(ref.get());
}
}
如果此时内存空间充足,那么对象将不会回收,如果空间不充足,则会进行回收。
public class TestDemo{
public static void main(String[]args){
Object obj=new Object();
String str="hello";
obj=null;//断开连接
SoftReference<Object> ref=new SoftReference<Object><obj>;//软引用
try{
for(int x=0;x<Inter.MAX_VALUE;x++){
str+=str+x;
str.intern();
}
}catch(Throwable e){
}
System.out.println(ref.get());
}
}
●弱引用:无论内存够不够,下一次内存收集之前都会进行;
弱引用本质的含义指的是说只要一进行GC处理,那么所引用的对象将会被立刻回收。弱引用需要使用的是Map接口的子类:java.util.WeakHashMap。
范例:观察弱引用
package cn.test.demo;
import java.lang.ref.SoftReference;
public class TestDemo{
public static void main(String[]args){
String key=new String(“hi”);
String value=new String(“hello”);
Map<String,String> map=new WeakHashMap<String,String>();
map.put(key,value);
System.out.println(map.get(key));
key=null;
System.out.println(map);
System.gc();
System.out.println(map);
}
}
一旦出现GC,则必须进行回收处理,而且一回收一个准。
HashMap与WeakHashMap区别?
HashMap是强引用,而WeakHashMap是弱引用。
在java.lang.ref包中存在有一个WeakReference的一个子类。
范例:观察WeakReference
package cn.test.demo;
import java.lang.ref.SoftReference;
public class TestDemo{
public static void main(String[]args){
String key=new String(“hi”);
WeakReference<String> map=new WeakHashMap<String>(key);
Key=null;
System.out.println(ref.get());
System.gc();
System.out.println(ref.get());
}
}
弱引用之所以不敢轻易使用的原因,就是因为其本身一旦有了GC之后就会立刻清空,这个对于程序的开发不利。