Java堆内存垃圾回收机制(GC)详解 (引用计数法,根搜索法详解)
来源:互联网 发布:java 异或加密 字符串 编辑:程序博客网 时间:2024/06/06 06:11
什么对象需要回收
在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之后就会立刻清空,这个对于程序的开发不利。
- Java堆内存垃圾回收机制(GC)详解 (引用计数法,根搜索法详解)
- Java垃圾回收(GC)机制详解
- Java垃圾回收机制(GC)详解
- Java垃圾回收机制(GC)详解
- Java垃圾回收机制(GC)详解
- Java虚拟机5:Java垃圾回收(GC)机制详解
- Java虚拟机:Java垃圾回收(GC)机制详解
- 详解Java 垃圾回收机制(GC)工作原理
- C#.Net 垃圾回收机制GC详解
- C#.Net 垃圾回收机制GC详解
- Java 垃圾回收机制 [ 内存管理 GC]
- java垃圾回收机制详解
- java垃圾回收机制详解
- Java垃圾回收机制详解
- java垃圾回收机制详解
- Java垃圾回收机制详解
- java垃圾回收机制详解
- java垃圾回收机制详解
- 设计模式 学习笔记 之 模板模式 Template Method(3)
- android TV端app开发的踩坑之旅
- 干货 | 高盛:2017人工智能报告中文版(附PDF版下载)
- 编译包含隐藏API的Android SDK(被@hide隐藏)
- 【设计模式】状态模式(State Pattern)
- Java堆内存垃圾回收机制(GC)详解 (引用计数法,根搜索法详解)
- MySQL,PHP语法,函数的不同使用举例时间戳和分类统计
- 安卓动画的简单实现方法(Animation和AnimationDrawable)
- eclipse安装插件注意事项
- [Java面试六]SpringMVC总结以及在面试中的一些问题
- 浏览器多个标签页通信
- 十分钟入门RocketMQ
- 895C
- (一)Angular 快速入门