JVM——GC
2016-04-22 11:44
211 查看
对象存活判定 |
在垃圾回收之前,进行的比较重要的事情就是对象“死活”的判定,要做到不能冤枉一个“好对象”,也绝不放过一个“死对象”,在现实生活中也是十分困难的,我们来看看在JVM中是怎么样做到的。
引用计数器算法
给每个对象添加一个引用计数器,当增加引用时,引用计数器+1,当删除引用时,计数器-1,当计数器=0的时候,说明这个对象已经没有引用了,可以视为删除的目标。优点:实现简单,效率高效
缺点:对象之间存在互引用,A引用B,B同时也引用A,但是这整个已经没用了,但是也是不能清理,因为计数器!=0.在实际的使用中,使用较少。
可达性分析算法
基本思想:算法通过一个叫“GC Root”的根去向下访问其他对象,访问的路径叫做引用链,如果某个对象不能到达“GC Root”(没有任何引用链相连),那么这个对象就是未被使用的对象,就是可以回收的。如下图5 6 7就是可以回收的。这种算法在实际中很常用。
引用的分类 |
引用类别:强引用,弱引用,软引用,虚引用。
强引用:在java中,强引用普遍存在,通过Object obj = new Object()这样申明的引用都是强引用,GC在任何情况下都不会回收这种引用的对象。
软引用:用来描述一些还有用但是非必须的对象。对于软引用关联的对象,系统发生OutOfMemoryError之前,会把它们列入回收的对象,对其进行二次回收,如果内存还是不够,抛出OutOfMemoryError,通过SoftReference类来实现软引用。
弱引用:也是用来描述非必需对象,但是它的强度比软引用更弱一些;被引用的对象只能生存到下一次垃圾回收之前;当GC时,无论内存够否,都会回收掉这类引用的对象。WeakReference类实现。
虚引用:幽灵引用或者幻影引用,一个对象有虚引用完全对其生存时间没有影响,也无法通过一个虚引用来获取一个对象的实例,设置其唯一的作用就是在系统回收对象前,可以收到系统通知。PhantomReference类来实现。
对象死不死?
对象在被可达性算法分析为没有引用链与GC root相连之后,GC并没有马上去回收它,而是先进行一次标记,意味着你要准备接收死刑了,在第一次标记之后,会对对象的finalize方法进行调用,如果对象中覆盖了finalize方法,还可以在其中进行自救,自救的方式就是与GC root上的对象发生直接关系,让自己有到达GC root的引用链,如果没有在finalize中进行自救,那么就行第二次标记,宣告死亡。方法区的垃圾回收
一般在新生代的堆中进行垃圾回收,可以率可以达到70%~90%,但是在方法区回收垃圾效率很低,方法区中垃圾回收主要收集不用的常量和无用的类;废弃常量判定:没有变量使用的常量,就可以回收,
无用的类判定:类的所有实例都被回收了,加载类的classLoader已经被回收了,该类的对应的java.lang.Class未被任何地方使用,不能通过反射机制来使用类的变量和方法。
垃圾收集算法 |
标记-清理
标记:如上所述,最少两次标记,不在赘述。清理:将标记的对象进行清理。
缺点:效率不高,而且在清理过后,会有很对内存碎片,不利于大对象的分配。
停止-复制
将内存空间按容量分为两块,每次只使用其中的一块,每次进行垃圾回收,只需要将还存活的对象复制到另外一块空的内存,然后把当前块清空即可。优点:简单高效
缺点:将内存空间大了对折
标记-整理
类似于标记-清除,但后续步骤不是直接进行清理,而不是先将存活对象移向一端,然后再进行清理,这样可以保证不会产生内存碎片。分代收集算法
只是将java堆分为了新生代和老年代,根据各自的特点,新生代使用“停止-复制”算法,老年代使用“标记-整理”或者“标记-清理”算法。内存分配与回收策略 |
老年代:大对象直接分配在老年代上,长期存活的对象就被转移至老年代,没经历一次Minor GC,对象的年龄就长一岁,当到达一定年龄(默认是15岁)的时候,就会将对象转移到老年代;还有就是相同年龄的对象占用了survivor空间的一半以上,无须等到年龄满足,系统会自动将>=该年龄的对象移至老年区;
从年轻代空间(包括 Eden 和 Survivor 区域)回收内存被称为 Minor GC。
当 JVM 无法为一个新的对象分配空间时会触发 Minor GC,比如当 Eden 区满了。所以分配率越高,越频繁执行 Minor GC。
内存池被填满的时候,其中的内容全部会被复制,指针会从0开始跟踪空闲内存。Eden 和 Survivor 区进行了标记和复制操作,取代了经典的标记、扫描、压缩、清理操作。所以 Eden 和 Survivor 区不存在内存碎片。写指针总是停留在所使用内存池的顶部。
执行 Minor GC 操作时,不会影响到永久代。从永久代到年轻代的引用被当成 GC roots,从年轻代到永久代的引用在标记阶段被直接忽略掉。
质疑常规的认知,所有的 Minor GC 都会触发“全世界的暂停(stop-the-world)”,停止应用程序的线程。对于大部分应用程序,停顿导致的延迟都是可以忽略不计的。其中的真相就 是,大部分 Eden 区中的对象都能被认为是垃圾,永远也不会被复制到 Survivor 区或者老年代空间。如果正好相反,Eden 区大部分新生对象不符合 GC 条件,Minor GC 执行时暂停的时间将会长很多。
所以 Minor GC 的情况就相当清楚了——每次 Minor GC 会清理年轻代的内存。
Major GC 是清理老年代。
Full GC 是清理整个堆空间—包括年轻代和老年代。
首先,许多 Major GC 是由 Minor GC 触发的,所以很多情况下将这两种 GC 分离是不太可能的。另一方面,许多现代垃圾收集机制会清理部分永久代空间,所以使用“cleaning”一词只是部分正确。
Major GC VS Full GC
大家应该关注当前的 GC 是否停止了所有应用程序的线程,还是能够并发的处理而不用停掉应用程序的线程。
相关文章推荐
- 关于测试工具以及前端性能测试的一些思考
- Linux CentOS 7 & Apache Tomcat 7 安装与配置
- Ubuntu 12.04下安装OpenCV 2.4.2 和 ffmpeg-0.11.1
- 知道这20个正则表达式,能让你少写1,000行代码
- 数位DP
- 8(进程控制)
- Social 实现分享功能
- c++第四次试验——作业
- iOS 集成极光推送,绕过一些坑
- C++第4次实验
- 基于归并排序的实现与应用
- TableView基础篇
- Docker安装
- ArrayList 源码详解
- android 简单联网判断
- 常用正则表达式
- 【UNET自学日志】Part10 摧毁玩家
- href target的作用
- 轻松搞定二叉树中的面试题
- 规则引擎简介