您的位置:首页 > 其它

JVM之垃圾收集器与内存分配策略

2017-10-27 17:47 465 查看
垃圾收集器进行收集前,首先要判断这些对象中哪些还活着哪些已经死去

引用计数器法:给对象添加一个引用计数器,每有一个地方引用时,加1,引用失效时,减1,计数器为0时,该对象必然无法被使用。实现简单,判定效率高,但Java虚拟机并未采用,因为它很难解决对象间循环引用的问题。

可达性分析算法:以“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连接,则该对象不可达。

可以作为GC Root的对象包括下面几种:

虚拟机栈(栈帧中的本地变量表)中引用的对象。

方法区中类静态属性引用的对象。

方法区中常量引用对象。

而引用又可以分为一下四种:

强引用 代码中普遍存在类似 Object obj=new Object()这类引用,只要强引用还在,垃圾收集器永远不会回收被引用的对象

软引用 描述一些还有用但并非必须存在的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出的异常。JDK1.2后,提供了SoftReference来实现软引用

弱引用 描述非必须对象,强度比软引用更弱一些,被弱引用关联的对象只能生存到下次GC之前。GC时,无论当前内存是否足够,都会回收只被弱引用关联的对象。JDK1.2后,提供WeakReference来实现弱引用。

虚引用 是最弱的引用关系 一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。JDK1.2后,提供了PhantomReference来实现虚引用。

一个对象死亡,至少经历两次过程,先是看可不可达,若不可达,则判断finalize()是否执行,finalize只执行一次,若未覆盖视为不执行。事实上,该方法不建议使用,开销极大,且不稳定,只是提一下。

方法区(永久代)主要回收两部分内容:废弃常量和无用的类。回收废弃常量与回收Java堆中的对象十分相似。废弃常量如字符串,其他类(接口),方法,字段符号的引用与此类似。

而判断一个类是否是无用的类的条件则苛刻许多。需要同时满足以下三个条件:

该类所有以加载的实例都以回收,Java堆中不存在该类的任何实例。

加载该类的classloader已经被回收。

该类对应的java.lang.class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

虚拟机可以对满足以上3个条件的无用类进行回收,仅仅是可以,并不一定会回收,是否回收,HotSpot提供了-Xnoclassgc参数进行控制,还可以使用-verbose:class以及-XX:+TraceClasssLoading,-XX:+TraceClassUnLoading查看类加载和卸载信息。

在大量使用反射,动态代理,CGlib等ByteCode框架,动态生成JSP以及OSGi这类频繁自定义ClassLoader的场景都需要虚拟机具备类型卸载的功能,以保证永久代不会溢出。

垃圾收集算法

标记-清除算法(Mark-Sweep)

顾名思义 先标记后清除

两个缺点:效率 标记和清除的效率都不高;空间 会产生大量不连续的碎片,可能导致大对象无法分配,而提前触发GC

复制算法(Copying)

将内存划分为一块较大的Eden空间和两块较小的Survior空间,每次使用Eden和其中一块Survivor。当回收时,将Eden和Survivor中还存活的对象一次性复制到另外一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间 hotspot默认E S比为8:1

数据表明,新生代中98%的对象都是朝生夕死,但没法保证每次回收都只有不多于10%的对象存活,当S不够用时,需要依赖老年代进行分配担保。

标记-整理算法(Mark-Compact)

标记过程与MS一样,但之后并不是直接清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

分代收集算法

并不是什么新算法,只是把内存划分为几块,如新生代,老年代,不同块采用不同上述收集方法。

HotSpot算法实现

GC时必须停顿所有执行线程(sun称之为Stop the World) 即时在号称几乎不会发生停顿的CMS中,枚举根节点也是必须要进行停顿的。

借助OopMap,safepoint,saferegion快速完成GC Root枚举

垃圾收集器

Young generation

Serial ParNew Parallel Scavenge G1

Tenured generation

CMS Serial Old Parallel Old G1

Serial

单线程的收集器,历史最悠久

ParNew

Serial的多线程版本常用CMS配合工作

Parallel Scavenge

更注重吞吐量,主要适合在后台运算而不需要太多交互的任务。

-XX:+UseAdaptiveSizePolicy可自动进行优化

Serial Old

Serial的老年代版本

Parallel Old

Parallel Scavenge的老年代版本

CMS

以获取最短回收停顿为目标,基于标记清除

整个过程分为4个步骤

初始标记

并发标记

重新标记

并发清除

初始标记与重新标记仍需Stoptheworld,但整个过程中耗时最长的并发标记和并发清除过程收集器线程都可以与用户线程一起工作。

3个缺点:

吃cpu 无法处理浮动垃圾,可能会出现Concurrent Mode Failure 失败,临时启用Serial Old进行老年代收集 第三个即MS的缺点

G1

新收集器,基于Region碰到在深入研究吧。

待续
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  内存分配 jvm