zoukankan      html  css  js  c++  java
  • Java中的GC操作及相关概念

    一、GC Roots Tracing的基本思路:通过一系列名为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所经过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链(用图论来说就是GC Roots到这些对象不可达)时,证明这些对象已经不可用
    二、Java中,可以作为GC Roots的对象包括以下几种
    1 虚拟机栈(桢栈中的本地变量表)中的引用的对象
    2 方法区中的类静态属性引用的对象
    3 方法区中的常量引用的对象
    4 本地方法栈中JNI(即一般说的Native方法)的引用对象
    三、Java对象的4种引用类型
    1 强引用: 如"Object object=new Object();", 那object就是一个强引用了,只要强引用还存在,GC就不会回收被引用的对象
    2 软引用: 用来描述一些还有用,但并非必需的对象
      注:对于软引用关联的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围并进行二次回收操作,如果这次回收还是没有足够的内存才会抛出内存溢出常,
      Jdk提供了SoftReference来实现软引用
    3 弱引用:也是用来描述非必需的对象,但它的强度比弱引用更弱一些,被弱引用关联的对象只能存活到下一次GC回收操作之前,
      Jdk提供了WeakReference来实现弱引用
    4 虚引用(幽灵引用或幻引用):一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例,为一个对象设置虚引用关联的唯一目的是希望当该对象被GC回收时收到一个系统通知
     Jdk提供了PhantomReference来实现虚引用

    四、宣告对象死亡至少需要经历两次标记,如果对象在进行根搜索后发现没有与GC Roots相连接的引用链,那它将会被第一次标记并进行一次筛选,筛选的条件是该对象是否有必要执行finalize()方法,当对象没有覆盖finalize()方法或finalize()方法已经被虚拟机执行过,虚拟机将这两种情况都视作“没必要执行”;如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会被放置在一个名为F-Queue的队列中,并在稍后由一条虚拟机自动建立的,低优先级的Finalizer线程去执行,稍后GC将对F-Queue中的对象进行第二次小规模的标记,如果对象要在finalize()中成功拯救自己--只要重新与引用链上的任何一个对象建立关联即可,譬如把自己(this关键字)赋给某个类变量或对象的成员变量,那在第二次标记时它将会被移出“即将回收”的集合

    五、一个类成为“无用类”需要满足的三个条件
    1 该类的所有实例都已经被回收,也就是说Java堆中不存在该类的任何实例
    2 加载该类的ClassLoader已经被回收
    3 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法

    六、垃圾收集算法
    1 标记--清除算法:该算法分为“标记”和“清除”两个阶段,首先标记出需要回收的对象,在标记完成后收掉所有被标记的对象,是最基础的算法,但存在以下两个缺点
    (1)效率问题:标记和清除过程的效率都不高
    (2)空间问题: 标记清除后会产生大量不连续的内存碎片,碎片太多导致当需要申请连续的大片空间的时候如果没有可用空间需要再触发一次垃圾收集动作

    2 复制算法: 它将可用内存按容量大小分为相等的两块,每次只使用其中的一块,当这一块的内存用完了就将还存活的对象复制到另一块上去,然后再把已使用过的那一半内存空间一次性清理掉
     优点:每次只是对其中的一块进行内存回收,内存分配时不用考虑内存碎片等复杂问题,只要移动堆顶指针按顺序分配内存即可,实现简单、运行高效
     缺点:将内存缩小了一半,代价太高

    3 标记整理算法: 标记过程仍然与“标记--清除算法”一样,但后续步骤不是对可回收对象进行清理,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存

    4 分代收集算法: 根据对象存活周期不同将内存分为几块,一般是把Java堆分为新生代和老年代,不同的区采用不同的垃圾收集算法,新生代中由于大量对象死去,只有少量存活,一般选用复制算法,老年代中因为对象存活率高没有额外空间进行分配担保,就必须使用“标记--清除”或“标记--整理”算法

     七、垃圾收集器
    1 Serial收集器:这个收集器是一个单线程的收集器,它的“单线程”的意义不仅仅说明它只有一个CPU或只有一条收集线程去进行垃圾收集工作,更重要的时在进行垃圾收集时,必须暂停其它所有的工作线程,直到垃圾收集结束

    2 ParNew收集器:该收集器是Serial收集器的多线程版本,除了使用多条线程进行垃圾收集外,其它的包括Serial可用的控制参数,收集算法等基本上一样

    3 Parallel Scavenge收集器(吞吐量优先收集器): 它是一个新生代收集器,使用复制算法且是并行的多线程收集器
    (1)Parallel Scavenge的特点是它的关注点与其它收集器不同,CMS(Concurrent Mark Sweep)等收集器的关注点是尽可能缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标是达到一个可控的吞吐目量(Throughput)
    (2)吞吐量是CPU用于运行用户代码的时间与CPU总消耗时间的比例,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)
    (3)高吞吐量可以最高效地利用CPU时间,主要适用在后台运算而不需要太多用户交互的任务
    (4)Parallel Scavenge用于精确控制吞吐量的两个重要参数:-XX:MaxGCPauseMillis(控制最大垃圾收集停顿时间)和-XX:GCTimeRatio(设置吞吐量大小)

    4 Serial Old收集器: Serial收集器的老年代版本,它同样是单线程收集器,使用“标记--整理”算法

    5 Parallel Old收集器:它是Parallel Scavenge收集器的老年代版本,使用多线程和“标记--整理”算法

    6 CMS(Concurrent Mark Sweep)收集器: 它是一种以获取最短回收停顿时间为目标的收集器,尤其适用于Java应用服务器或B/S服务器,它基于“标记--清除”算法来实现
    (1)CMS收集器的运作过程包括以下4个步骤: 
    A 初始标记(CMS initial mark): 仅仅标记一下GC Roots能直接关联到的对象,速度很快
    B 并发标记(CMS concurrent mark): 进行GC Roots Tracing的过程
    C 重新标记(CMS remark): 修正并发标记期间因用户程序继续运作而导致标记变动的那一部分对象的标记记录,这个阶段停顿时间一般比初始标记阶段长一些,但远比并发标记阶段短
    D 并发清除(CMS concurrent sweep)
    (2)初始标记和重新标记两个步骤仍然需要停止用户线程
    (3)整个过程中耗时最长的并发标记和并发清除过程中,收集器线程可以与用户线程一起工作,所以总体上来讲CMS收集器的回收过程是与用户线程一起并发执行的
    (4)CMS收集器的三个显著缺点
    A CMS收集器对CPU资源非常敏感
    B CMS收集器无法处理浮动垃圾(Floating Garbage),可能出现"Concurrent Mode Failure"失败而导致另一次Full GC操作,另外,由于垃圾收集阶段用户线程还需要继续运行,即还需要预留足够的内存空间给用户线程使用,因此CMS无法等到老年代几乎完全被填满再进行垃圾收集,需要预留一部分空间供并发收集时的用户线程使用,要是CMS运行期间预留的内存无法满足程序需要就会出现一次“Concurrent Mode Failure”失败
    C CMS收集结束时会产生大量的内存碎片,因为CMS是基于“标记--清除”算法的

    7 G1收集器(Garbage First): 它是当前收集器发展的最前沿成果
    (1)G1收集器与CMS收集器相比有两个显著的改进
     A  G1收集器是基于“标记--整理”的算法实现的收集器,它不会产生空间碎片
     B  它可以精确地控制停顿,即能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不会超过N毫秒
    (2)G1收集器可以在基本不牺牲吞吐量的前提下完成低停顿的内存回收,这是由于它能极力地避免全区域垃圾收集,之前的垃圾收集器进行收集的范围都是整个新生代或老年代,而G1将整个Java堆(包括新生代和老年代)划分为多个大小固定的独立区域(Region),并且跟踪这些区域里的垃圾堆积程度,在后台维护一个优先列表,每次根据允许的收集时间,优先收集垃圾最多的区域

    八、HotSpot JVM1.6的垃圾收集器
    说明:如果两个垃圾收集器之间有线相连,说明这两款垃圾收集器可以搭配使用

    九、内存分配的5条基本原则
    1 对象优先在Eden区分配: 大多数情况下,对象在新生代的Eden区中分配,当Eden区没有足够的空间进行分配时会发生一次Minor GC操作
    2 大对象直接进入老年代: 大对象是指需要大量连续内存空间的Java对象,虚拟机提供了一个-XX:MaxTenuringThreshold参数令大于这个设置值的对象直接在老年代中分配空间,这样做的目的是避免在Eden区及两个Survivor区之间大量的对象拷贝
    注:-XX:MaxTenuringThreshold只对Serial和ParNew两款收集器有用
    3 长期存活的对象将直接进入老年代: 虚拟机给每个对象定义了一个对象年龄(Age)计数器,如果对象在Eden区出生并经过第一次Minor GC后仍然存活并且能被Survivor容纳的话,将被移到到Survivor区并将对象年龄设为1,对象在Survior每熬过一次Minor GC年龄就加1,当它的年龄达到-XX:MaxTenuringThreshold的设置值后就会被晋升到老年代中
    4 动态对象年龄判断: 为了更好地适应不同程序的内存状况,虚拟机并不总是要求对象年龄达到-XX:MaxTenuringThreshold设置值才能晋升老年代,如果在Survivor中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年区,无须等到年龄达到-XX:MaxTenuringThreshold设定的值
    5 空间分配担保: 在发生Minor GC时,虚拟机会检测之前晋升到老年代的平均大小是否大于老年代的剩余空间大小,如果大于则直接进行一次Full GC操作,如果小于则看HandlePromotionFailure设置是否允许担保失败,如果允许就进行一次Minor GC,否则进行一次Full GC,当出现大量对象在Minor GC后仍然存活的情况时(最极端的情况是内存回收后新生代中所有对象都存活)就需要老年代进行分配担保,让Survivor无法容纳的对象直接进入老年代

     
     
  • 相关阅读:
    cookie 保存信息 例子
    win7 远程桌面 不用域账户登录
    jfreechart demo 源代码 下载
    PostgreSQL学习手册(数据表)
    apache tomcat 伪静态 struts2 伪静态
    List methods
    DX 骨骼动画
    如何成为一名优秀的程序员?
    程序员改编游戏向女友求婚
    引用 病毒是怎么命名的?教你认识病毒命名规则
  • 原文地址:https://www.cnblogs.com/ceshi2016/p/6077871.html
Copyright © 2011-2022 走看看