深入理解JVM虚拟机读书笔记【第三章】垃圾收集器与内存分配策略

【第三章】垃圾收集器与内存分配策略

3.1 - 概述

3.2 - 对象已死?

1.垃圾收集器会收集堆中已经死去的对象,即不可能再被任何途径使用的对象。

3.2.1 - 引用计数算法

原理:

1.给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器都为0的对象就是不可能再被使用的。

缺点:

1.不能解决对象之间的相互循环引用的问题。(会导致对象无法回收,产生内存泄漏)

3.2.2 - 根搜索算法【虚拟机用该算法】

原理:

1.通过一系列的名为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连(用图论的话来说就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。

可作为GC Roots的对象:

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

2.方法区中的类静态属性应用的对象。

3.方法区中常量引用的对象。

4.本地方法栈中JNI(即一般说的Native方法)的引用的对象。

3.2.3 - 再谈引用

引用的定义:

1.【JDK1.2之前】如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称这块内存代表着一个引用。【定义过于狭隘】

引用的分类:

1.强引用

概念:就是指在程序代码中普遍存在的,类似"Object obj = new Object()"这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。

垃圾收集器相关:垃圾收集器永远不会回收掉被引用的对象。

2.软引用

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

垃圾收集器相关:

2.1.系统将要发生内存溢出异常之前。

2.2.将被软引用的对象列进回收范围之内。

2.3.进行第二次回收。

2.4.回收后还是没有足够的内存,抛出内存溢出异常。

3.弱引用

概念:也是用来描述非必需对象的,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK1.2之后,提供了WeakReference类来实现弱引用。

垃圾收集器相关:当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。

4.虚引用【也叫幽灵引用或者幻影引用】

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

3.2.4 - 生存还是死亡?

1.在根搜索算法中不可达的对象,也并非是"非死不可"的,这时候他们暂时处于"缓刑"阶段。要真正宣告一个对象死亡,至少要经历两次标记过程。

2.如果对象在进行根搜索后发现没有与GC Roots相连接的引用链,那它将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,虚拟机将这两种情况都视为"没有必要执行"。

注意点:

finalize(),没有用,缺点太多,有try catch f 替代

3.2.4 - 回收方法区【永久代?】

1.主要收集废弃常量和无用的类。回收废弃常量与回收Java堆中的对象非常类似。

2.判定一个类是无用的类。要同时满足下面3个条件。

   2.1.该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例。

   2.2.加载该类的ClassLoader已经被回收。

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

3.虚拟机可以对满足上述3个条件的无用类进行回收,这里说的仅仅是"可以",而不是和对象一样,不使用了就必然会回收。

4.在大量使用反正,动态代理,CGLib等bytecode框架的场景,以及动态生成JSP和OSGI这类频繁自定义ClassLoader的场景都需要虚拟机具备类卸载的功能,可以保证永久代不会溢出。

3.3 - 垃圾收集算法

3.3.1 - 标记-清除算法

原理:

1.首先标记出所有需要回收的对象             

2.在标记完成后统一回收掉所有被标记的对象。

缺点:

1.效率相关-效率不高,标记和清除过程效率都不高。

2.空间相关-标记清除之后会产生大量不连续的内存碎片,空间碎片太多,可能会导致,当程序在以后的运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。

执行过程:

3.3.2 - 复制算法【用于回收新生代】

原理:

1.将内存划分为大小相等的两块。每次只使用一块。

2.当这一块的内存使用完了就将还活着的内存复制到另外一块上面,然后在把已使用过的内存空间一次清理掉。

缺点:

1.内存浪费量大。

2.在对象存活率较高时就要执行较多的复制操作,效率将会变低。

执行过程:

 

特点:

1.IBM研究表明,新生代中的对象98%是朝生夕死的,所有不用按照1:1来划分内存空间。

2.内存被分为一块较大的Eden空间。和两块较小的Survivor空间。

3.每次使用Eden和其中的一块Survivor空间。

4.当回收时,将Eden和Survivor中还存活着的对象一次性地拷贝到另外一块Survivor空间上。最后清理掉用过的Eden和Survivor

3.3.3 - 标记整理算法

1.老年代不选用这种算法。

2.让所有存活的对象都向一端移动,然后直接清理掉端边以外的内存。

原理如图:

3.3.4 - 分代收集算法

1.根据对象的存活周期的不同,将内存划分为不同的几块。【一般分新生代,老年代】

2.在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法。【只需要付出少量存活对象的复制成本就可以完成收集】

3.而老年代中因为对象存活率高,没有额外空间对他进行分配担保,就必须使用"标记 - 清理" 或 "标记 - 整理"算法来进行回收。

3.4 垃圾收集器【垃圾回收算法的具体实现】

1.JDK 1.6的垃圾收集器。【如果两个收集器之间存在连线,就说明他们可以搭配使用】

3.4.1 Serial 收集器【会产生Stop The World:暂停其它所有工作线程,直到垃圾收集完毕】

应用场景:Client模式下的虚拟机来说是一个很好的选择。

1.最基本,历史最悠久的收集器。

2.是新生代收集的唯一选择。是单线程的收集器。

3.是单CPU环境中最好的收集器。

运行过程:

3.优点:

3.1简单而高效(与其他收集器的单线程比)。原因:对于单个CPU环境,没有线程交互的开销,专心做垃圾收集,效率自然高。

3.4.2 ParNew 收集器【会产生Stop The World】

1.是Serial收集器的多线程版本,其他的与Serial收集器完全一样。

2.是许多运行在Server模式下的虚拟机中首选的新生代收集器。

3.重要:除了Serial收集器以外,目前只有它能与CMS收集器配合工作。

4.在单CPU的环境中,性能不会比Serial高。由于存在线程交互的开销,在2个超线程实现的CPU环境中,未必能100%超越Serial收集器。如果可使用CPU越来越多的话,它对系统的资源利用还是很有好处的。

5.默认开启的收集线程数与CPU的数量相同。

6.并发并行的名词解释:

7.执行过程如下:

3.4.3 Parallel Scavenge 收集器【会产生Stop The World】【支持自适应】

特点:吞吐量优先收集器,可以设置GC自适应的调节策越。

1.新生代收集器,用复制算法收集,是并行的多线程收集器。

2.目的:达到一个可控制的吞吐量。吞吐量就是:CPU用于运行用户代码的时间与CPU总消耗时间的比值。公式:吞吐量=运行用户代码的时间 / (运行用户代码的时间 + 垃圾回收时间)。例:虚拟机总共运行了100分钟,其中垃圾收集花掉了一分钟,那吞吐量就是99%。

3.停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户的体验;而高吞吐量则可以最高效率地利用CPU时间,尽快地完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。

4.MaxGCPauseMillis调小了,垃圾回收的次数变多了,吞吐量就下降了。

5.GCTimeRatio取值在0 - 100之间,如果值为19,那GC的时间占总时间的5%(即 1 / (1 + 19)).默认值为99,即GC时间占总比的1%,(1 / (99 + 1))。

3.4.4 Serial Old 收集器【会产生Stop The World】

1.是Serial收集器的老年代版本,单线程收集器,用"标记 - 整理"算法。Client模式下用。

2.执行过程:

3.4.5 Parallel Old 收集器【会产生Stop The World】

1.是Parallel Scavenge收集器的老年代版本,使用多线程和"标记 - 整理"算法。

2.在注重吞吐量及CPU资源敏感的场合,可以优先考虑Parallel Scavenge加Parallel Old收集器组合。

3.执行过程:

3.4.6 CMS 收集器【会产生Stop The World】

1.目的:尽可能地缩短垃圾收集时用户线程的停顿时间。

2.适合重视服务的响应速度的场合。

3.基于"标记 - 清除"算法实现的。

4.整个过程分4个步骤:

4.1初始标记。【需要Stop The World】

    4.1.1仅仅只是标记一下GC Roots能直接关联到的对象。速度很快。

4.2并发标记。【需要Stop The World】

    4.2.1就是进行GC Roots Tracing的过程。

4.3重新标记。     

    4.3.1为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录。停顿时间比初始标记长,比并发标记时间短。

4.4并发清除。

5.执行过程:

6.缺点:

6.1对CPU资源非常敏感。

6.1无法处理浮动垃圾。

6.3---------------------------------

3.4.7 G1 收集器【会产生Stop The World】  

1.不会产生空间碎片,对长时间运行的应用系统来说非常重要。

2.可以精准地控制停顿。

3.可以实现在基本不牺牲吞吐量的前提下完成低停顿的内存回收。

3.4.8垃圾收集器参数总结

3.5内存分配与回收策略

3.5.1对象优先在Eden分配

示例:

3.5.1大对象直接进入老年代

3.5.3 长期存活的对象将进入老年代

3.5.4动态对象年龄判定

3.5.5 空间分配担保

3.6本章小结

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值