JVM垃圾收集

判断对象是否存活

  Java内存运行时中的程序计数器、虚拟机栈(也就是栈)、本地方法3个区域的内存是随线程的终止而释放,这几个区域不需要过多的考虑回收的问题。而 Java 堆和方法区不一样,只有程序处于运行期间才能知道会创建哪些对象,这部分内存的分配和回收都是动态的,垃圾收集器所关注的也是这部分内存。
  垃圾收集器在对堆进行回收之前,如何判断对象是否”存活“?
  > 引用计数算法:创建对象时,同时会产生一个引用计数器,每当有一个地方引用它时,计数器+1;当引用销毁时,计数器-1,任何时刻引用计数器为 0 的对象,标志着这个对象已经没有引用,可以回收。
  主流的Java虚拟机里面没有选用引用计数法来管理内存,主要原因是它很难解决对象之间相互循环引用的问题。
  > 可达性分析算法:在主流的商用语言的实现中,都是采用此算法来判定对象是否存活,基本思想是,通过一系列称为”GC Roots” 的对象作为起点,向下开始搜索,搜索所走过的路径称为 “引用链”(Reference Chain),当一个对象到 GC Roots 没有任何引用链相连(用图论的话来说,从GC Roots 到这个对象不可达)时,则说明这个对象是不可用的。
 
这里写图片描述

图-1 可达性分析算法判定对象是否可回收

在Java语言中,可以作为GC Roots的对象包括下面几种:
- 虚拟机栈中引用的对象(本地变量表)
- 方法区中静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中引用的对象(Native对象)
无论怎样判断对象是否存活,都与对象的引用(Reference)有关,在JDK1.2之后,将引用分为强引用(Strong)、软引用(Soft)、弱引用(Weak)、虚引用(Phantom)4种,引用强度依次减弱。

类引用类型  

 - 强引用(Strong Reference):只要引用存在,垃圾回收器永远不会回收,如Object obj = new Object(); obj是一个强引用,只有引用释放后,对象才会被回收。
 - 软引用(Soft Reference):非必须引用,在发生内存溢出之前进行回收,可通过SoftReference类实现软引用,如:Object obj = new Object(); SoftReference< Object> sr = new SoftReference< Objetc>(obj);
obj = null; sr.get();
这时sr对obj是一个软引用,通过get()可以获取到这个对象,当这个对象被回收时,返回 null。
软引用主要用户实现类似缓存的功能,在内存足够的情况下直接通过软引用取值,无需从繁忙的真实来源查询数据,提升速度;当内存不足时,自动删除这部分缓存数据,从真正的来源查询这些数据
 - 弱引用(Weak Reference):第二次垃圾回收时回收,不管当前内存是否足够,可用WeakReference 类实现,如:Object obj = new Object(); WeakReference < Object> wr= new WeakReference < Objetc>(obj);
obj = null; wr.get(); wr.isEnQueued();//返回是否被垃圾回收器标记为即将回收的垃圾

弱引用是在第二次垃圾回收时回收,短时间内通过弱引用取对应的数据,可以取到,当执行过第二次垃圾回收时,将返回null。
  - 虚引用(Phantom Reference):虚引用是每次垃圾回收的时候都会被回收,通过虚引用的get方法永远获取到的数据为null,因此也被成为幽灵引用。虚引用主要用于检测对象是否已经从内存中删除

方法区(或永久代)的回收,永久代的垃圾回收主要是两部分:废弃常量无用的类
废弃常量:在系统中没有任何地方引用此常量。
无用的类:该类所有实例都被回收;加载该类的ClassLoader已被回收;该类对应的java.lang.Class对象没有任何地方被引用。

垃圾收集算法

  1. 标记-清除算法(Mark-Sweep)
    分为”标记“和”清楚“两个阶段:首先标记出所有需要回收的对象,标记完成后统一回收所有被标记的对象。主要由两个不足:一是标记和清除两个过程效率不高;而是标记清楚之后会产生大量不连续的内存碎片。
    这里写图片描述
    图-2 标记-清楚算法图(出自《深入理解Java虚拟机》)
  2. 复制算法(Copying)
    为了解决效率,复制算法将可用内存按容量分为大小相等的两块,每次只用其中的一块,当这一块的内存用完了,就将还存活着的对象复制到另一块上面,然后把使用过的内存一次清理掉,每次只是对整个半区进行回收,因此不必考虑内存碎片的问题。
    新生代中的对象大部分是”朝生夕死”的,不需要1:1的比例来划分内存空间,而是将内存分为一块较大的Eden空间和两块较小的Survivor空间(大小默认为 8:1,如新生代大小为100M,则Eden为80M,From Survivor 10M,To Survivor 10M),每次使用Eden和一块Survivor空间,当回收时,将Eden和刚才用过的Survivor中还存活的对象一次性复制到另一块Survivor空间上,然后清理掉Eden和刚才使用过的Survivor空间。
  3. 标记-整理算法(Mark Compact)
    复制算法在对象存活率较高时,效率将会变低(进行较多的复制操作),所有在老年代一般不能直接选用复制算法。
    标记-整理算法,标记和“标记-清除”算法一样,但后续步骤不直接回收可回收对象,而是让所有存活的对象都想一端移动,然后清理掉端边界以外的内存。
  4. 分代收集算法(Generational Collection)
    这个算法并没有新的思想,只是根据对象存活周期不同将内存划分为几块,如Java堆分为新生代和老年代,新生代采用复制算法;老年采用”标记-清除“或”标记-整理“。

垃圾收集器

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值