zoukankan      html  css  js  c++  java
  • [深入理解JVM虚拟机]第3章-垃圾收集器、内存分配策略

    垃圾收集器

    判断对象是否需存活

    • 回收堆
      • 判断对象是否存活:
        • 方法一:引用计数法。对象被引用一次就+1,当为0时回收对象。缺点:无法解决循环引用问题。
        • 方法二:可达性分析算法。记录当前对象是否有和GC Roots中对象的引用链。(其中,可以作为GCRoots对象的有:虚拟机栈中引用的对象、方法去中类静态属性引用的对象、方法区中常量引用的对象、本地方法栈中引用的对象。)
          • 不可达对象并不是一定被垃圾收集的,当这个对象有必要执行finalize()并finalize里自己和某个对象建立关联,即可在第二次标记时被移出“即将回收”的集合。但强烈建议不在finalize()里来拯救对象,使用try-finally等其他方式或许更好。
      • 引用分为:强引用(new出来的)、软引用(SoftReference类实现)、弱引用(WeakReference类实现)、虚引用(PhantomReference类实现)。是为了描述一类对象:内存空间还够的时候能保存在内存,不够就可以抛弃这些对象。
    • 回收方法区
      • 主要是回收方法区中的废弃常量无用的类
        • 废弃常量:假如在常量池中存在字符串 "abc",如果当前没有任何 String 对象引用该字符串常量,就说明常量 "abc" 就是废弃常量
        • 无用的类必须满足三点:Java堆中不存在该类实例;加载该类的ClassLoader已经被回收;该类对应的java.lang.Class对象没有在其他地方被引用。
        • 无用的类不一定要被回收,可以通过参数与进行控制。

    垃圾收集算法(内存回收方法论)

    • 分代收集算法:把堆对象分为新生代(存活率低的对象)和老年代(存活率高的对象)。
      • 老年代:
        • 方法一:标记-清除算法:把要回收的对象标记一下,然后清除。缺点:标记和清除两个操作效率低、会造成空间碎片可能导致二次回收。
        • 方法二:标记- 整理算法:把要回收的对象标记一下,然后留下的对象连续排。
      • 新生代:复制算法
        • 把堆内存分为两部分,只使用其中的一部分装对象,当装不进去后,把留存对象拷贝到另一部分,连续放置。
        • 两部分比例可以超过1:1比如9:1,但要使用内存担保,用老年代的内存空间担保,即1放不下时候放老年代去。所以只适合新生代使用。

    HotSpot的算法实现(HotSpot如何发起内存回收)

    • 关于枚举根节点:
      • HotSpot中,使用OopMap数据结构使虚拟机直接指导哪些地方存着对象引用,这样GC扫描时可以直接得知这些信息。
      • 具体地,类加载完成后,HotSpot就把对象内什么偏移量是什么数据类型算出来。JIT编译过程中,也会记录下栈和寄存器中哪些位置是引用。
    • 安全点:
      • 因为对象引用是变化的,所以OopMap是变化的,但不可能每一时刻都记录对应的OopMap,故只有在安全点才记录了OopMap,GC只有在安全点才回发生,使线程暂停。
      • 如何让所有线程跑到安全点才停下来?主流采用主动式中断,在安全点设置标志,各个线程主动去轮询到没到标志,到了的话就停下。
    • 安全区域:
      • 当程序不执行的时候,即没有分配CPU时间的时候,无法响应JVM的中断请求走到安全点挂起,这就需要安全区域。
      • 安全区域是指在一段代码片段中,引用关系不发生变化。
      • 当线程执行到安全区域,就标识进入安全区域,此时JVM发起GC就不用管这个线程了,当线程要离开安全区域时,检查是否已完成了GC,完成即可离开安全区域。

    垃圾收集器(内存回收的具体实现、特点)

    重点1:CMS(Concurrent Mark Sweep)收集器:老年代收集器

    • 并发的收集器
    • 以减少用户线程的停顿,加快响应速度为目标
    • 基于标记-清除算法
    • 步骤
      • 1 初始标记:标记和GC Roots直接关联的对象(stop worlds)。
      • 2 并发标记:垃圾回收线程和用户线程并发,进行可达性分析,标记所有和GC Roots相连的节点。
      • 3 重新标记:由于上一步可能会有标记更改,对这些更改进行重新标记(stop worlds)。
      • 4 标记清除:垃圾回收线程和用户线程并发,进行标记请清除
    • 缺点
      • 1 对CPU资源敏感。因为占用了一部分线程,所以虽然减少停顿,但会导致应用程序变慢,尤其是CPU资源少的时候。
      • 2 无法处理浮动垃圾。由于4标记清除是并发进行的,这一过程用户线程新产生的垃圾本次无法处理,并且要预留一部分内存空间给此时产生的垃圾,如果预留的不够,就会造成本次垃圾收集失败,导致再次GC。
      • 3 由于使用标记-清除算法,所以会产生内存碎片。

    重点2:G1 (Garbage-First) 收集器:自己可以负责全部的GC收集

    • G1是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器,以极高概率满足 GC 停顿时间要求的同时,还具备高吞吐量性能特征。
    • 特点
      • 可以不与其他垃圾收集器配合独自管理GC,但是在内部仍保留分代概念。
      • 整体基于标记-整理算法,局部基于复制算法。
      • 可预测的停顿时间:建立可预测的停顿时间模型,能让使用者明确指定stop worlds在一个长度为 M 毫秒的时间片段内。 内部实现:使用 Region 划分内存空间,G1 收集器在后台维护了一个优先列表,每次根据允许的收集时间,优先选择回收价值最大的 Region。保证了 GF 收集器在有限时间内可以尽可能高的收集效率(把内存化整为零)。
      • 并行与并发:可利用多颗cpu、多核优势,减少stop worlds 时间。并且支持并发。
    • 步骤
      • 初始标记
      • 并发标记
      • 最终标记
      • 筛选回收

    Serial收集器(新生代、单线程、复制算法、Client模式)、ParNew收集器(新生代、多线程、复制算法、Server模式)、Serial Old收集器(老年代、单线程、标记整理算法、Client模式)、CMS

    这其中的新生代可以与老年代搭配使用。

    Parallel Scavenge收集器(新生代、多线程、重吞吐、有自适应调节策略、复制算法) 、Parallel Old收集器(老年代、多线程、标记整理算法)

    两者可搭配使用。
    重吞吐是指应用程序总体完成时间短,但停顿可能大一些,适合后台、用户交互少的应用。

    理解GC日志

    GC日志包含:发生时间、停顿类型(GC/Full GC)、DefNew等表示GC发生的区域时新生代/老年代/永久代、GC前后内存总容量变化、堆内存容量变化。

    Minor GC 与 Major GC

    • 新生代 GC(Minor GC):指发生新生代的的垃圾收集动作,Minor GC 非常频繁,回收速度一般也比较快。
    • 老年代 GC(Major GC/Full GC):指发生在老年代的 GC,出现了 Major GC 经常会伴随至少一次的 Minor GC(并非绝对),Major GC 的速度一般会比 Minor GC 的慢 10 倍以上。

    内存分配

    todo

    参考连接

    https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/jvm/JVM垃圾回收.md#46-cms-收集器

  • 相关阅读:
    《设计模式
    JConsole监控远程Tomcat服务器
    Linux下Nginx+tomcat应用系统性能优化
    nginx 解决400 bad request 的方法
    lvs、haproxy、nginx 负载均衡的比较分析
    三种LVS负载均衡技术的优缺点----负载均衡调度算法
    LVS集群的体系结构
    LVS--什么是LVS?
    七、Nginx学习笔记七Nginx的Web缓存服务
    六、Nginx 防盗链
  • 原文地址:https://www.cnblogs.com/coding-gaga/p/11255289.html
Copyright © 2011-2022 走看看