• JVM垃圾回收分析


    GC和GC Tuning

    GCC基础知识

    什么是垃圾

    C语言申请内存:malloc free

    C++: new delete

    C/C++: 手动回收内存

    Java:

    自动内存回收,编程简单,系统不易出错,容易出现两种类型问题:

    1. 忘记回收
    2. 多次回收

    如何定位垃圾

    • 引用计数(RefrenceCount), 无法解决对象循环引用问题

    reference1

    • 根可达算法(RootSearching)

    20200721-gc_root_searching

    常见的垃圾回收算法

    • 标记清除(mark sweep)---位置不连续,容易产生内存碎片,碎片多的情况下导致空间利用率不高

    gcc_mark-sweep

    • copy算法---没有碎片,但是浪费空间

    gcc_copy

    • 标记压缩算法(mark compact)---没有碎片,效率低(两边扫描,指针需要调整)

    gc_mark_compact

    JVM内存分代模型

    gc_generator_model

    1. 部分垃圾回收器使用的模型

    除Epsilon ZGC Shenandoah之外的GC都使用逻辑分代模型

    G1是逻辑分代,物理不分代

    除此之外不仅逻辑分代,而且物理分代

    1. 新生代+老年代+永久代(1.7)Perm Generation/元数据区(1.8) Metaspace

      1. 永久代 元数据---Class
      2. 永久代必须指定大小限制,元数据可以设置,也可以不设置,无上限(受限于物理内存)
      3. 字符串常量1.7--永久代 1.8--堆
      4. MethodArea逻辑概念--永久代、元数据
    2. 新生代=Eden+2个survivor区

      1. YGC回收后,大多数对象会被回收,或者进入S0
      2. 再次YGC,活着对象Eden+S0->S1
      3. 再次YGC, Eden+S1->S0
      4. 年龄足够-->老年代(PS 15 CMS 6)
      5. s区装不下-->老年代
    3. 老年代

      1. 顽固分子
      2. 老年代满了则FGC /Full GC
    4. GC Tunning(Generation)

      1. 尽量减少FGC
      2. MinorGC=YGC
      3. MajorGC=FGC
    5. 对象分配过程

      gc_action

    6. 动态年龄(不重要)

      https://www.jianshu.com/p/989d3b06a49d

    7. 分配担保(不重要)

      YGC期间survivor区空间不够了,空间担保直接进入老年代

    常见的垃圾回收器

    gc_collector

    1. 垃圾回收器的发展路线是随着内存越来越大的过程而演进

      从分代算法演化到不分代算法

      Serial算法 几十M

      Parallel算法 几个G

      CMS 几十个G---承上启下,开始并发回收

      G1-上百个G

      ZGC-- 4T-16T (JDK13)

      三色标记

    2. JDK1.0只有Serial,后来为了提高效率诞生并发垃圾回收器,发展史

    3. Serial采用copy方式回收年轻代serial1

    4. Serial-Old采用标记压缩回收老年代

      serial2

    5. Parallel Scavenge 使用多线程采用copy方式回收垃圾

      para1

    6. Parallel old采用多线程压缩算法进行回收

      para2

    7. ParNew与PS一样,只是为了配合CMS使用

      parnew

    8. CMS

      1. concurrent mark sweep
      2. a mostly concurrent, low-pause collector
      3. initial mark(find GC ROOT), concurrent mark(可能会标错), remark(剔除标记错误的对象,不应该被标记), concurrent sweep
      4. 算法: 采用三色标记法+Incremental Update

      cms

      缺点:

      内存碎片,加入-XX:CMSFullGCsBeforeCompaction

      浮动垃圾

      ​ Concurrent Model Failure -XX: CMSInitiatingOccupancyFraction 92%

      当内存碎片具多时,CMS将采用Serial old方式回收垃圾

    9. G1(200ms-10ms)

      算法: 采用三色标记法+SATB

    10. ZGC(100ms-1ms) PK C++

    算法: ColoredPointers + LoadBarrier

    1. Shenandoah

      算法: ColoredPointers + WriteBarrier

    三色标记法

    白色:未被标记

    灰色:自身被标记但成员未被标记

    黑色:自身和成员都被标记

    产生漏标:

    • 标记进行时增加一个黑到白的引用,如果不重新对黑色进行处理,则会漏标
    • 标记进行时删除了灰色对象到白对象的引用,那么这个白对象有可能被漏标

    三色标记法

    常见的垃圾回收器组合参数设定

    • -XX:+UseSerialGC ===Serial New + Serial Old
    • -XX:+UseParNewGC ===ParNew + Serial Old
    • -XX:+UseConcurrentMarkSweepGC ===ParNew + CMS + Serial Old
    • -XX:+UseParallelOldGC ===PS + PO
    • -XX:+UseParallelGC ===PS + PO
    • -XX:+UseG1GC ===G1

    CMS三色标记

    20200721-three_color_cms

    JVM调优

    JVM命令行参数:https://blog.csdn.net/zhyhang/article/details/105037987

    试用程序

    public class HelloGC {
        public static void main(String[] args) {
            System.out.println("Hello GC!");
            LinkedList<Object> objects = new LinkedList<>();
            while (true) {
                byte[] b = new byte[1024*1024];
                objects.add(b);
            }
        }
    }
    
    1. 区分概念内存泄漏 memory leak,内存溢出out of memory
    2. java -Xmn10M -Xms40M -Xmx60M --XX:+PrintGC HelloGC
    3. java -XX:+PrintFlagsInitial 显示默认参数
    4. java -XX:+PrintFlagsFinal 显示最终参数

    调优的基础概念

    吞吐量:用户代码执行时间/(用户代码执行时间+垃圾回收时间)

    响应时间:STW越短,响应时间越好

    调优需要明确是吞吐量优先,还是响应时间优先,或者满足一定响应时间下,吞吐量越大越好。

    科学计算、数据挖掘一般要求吞吐量优先,可以选择PS+PO

    Web网站,RestAPI一般要求响应优先

    什么是调优

    根据需要进行JVM规划和预调优

    优化JVM运行环境(慢、卡顿等)

    解决JVM运行过程中出现的各种问题

    调优步骤

    1. 调优从业务场景开始,没有业务场景的调优就是耍流氓

    2. 无监控,不调优,系统只有通过压力测试,看到结果,才能明确调优方向

    3. 根据实际业务选择垃圾回收

    4. 计算内存需求

    5. 选定CPU,主频越高越好

    6. 设置年代大小,年龄升级的阈值

    7. 设置日志参数 -Xloggc:/path/to/xxx-%t.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5

      -XX:GCLogFileSize=20M -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCCause

    8. 观察日志

    优化环境

    有一个50w PV的资料网站(从磁盘读取文档到内存)原服务器32位、1.5G的堆内存,用户反馈网站反应慢,因此公司将服务升级为64位、16G堆内存,结果用户反映有时反应比以前更慢了?

    网站慢是因为很多用户浏览网站,需要将数据从文件load到内存,而内存过小导致频繁GC,STW时间过长,导致应用反应慢;换大内存反应还慢是因为内存大了后FGC的时间过长,因此需要调整垃圾回收器

    系统CPU经常100%,如何优化?

    1. 首先找出那个进程CPU使用率高(top)
    2. 找出该进程那个线程CPU使用率高(top -Hp)
    3. 导出该线程堆栈(jstack)
    4. 查看那个方法消耗时间(jstack)
    5. 工作线程占比高还是垃圾回收器占比高

    系统内存飙高,如何查找原因?

    1. 导出堆内存(jmap)
    2. 分析(jhat、jvisualvm、mat)

    解决JVM运行中的问题

    通过一个案例理解工具

    public class HelloGC {
        private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(50,
                new ThreadPoolExecutor.DiscardOldestPolicy());
    
        public static void main(String[] args) {
            executor.setMaximumPoolSize(50);
            while (true) {
                modelFit();
                try {
                    TimeUnit.MILLISECONDS.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        private static class CardInfo {
            BigDecimal price = new BigDecimal(0.0);
            String name = "Liming";
            int age = 25;
            Date birthday = new Date();
    
            public void m() {
                //do something
            }
        }
    
        private static void modelFit() {
            List<CardInfo> tasks = getAllCardInfo();
            tasks.forEach(e->executor.scheduleWithFixedDelay(e::m, 2 ,3, TimeUnit.SECONDS));
        }
    
        private static List<CardInfo> getAllCardInfo() {
            List<CardInfo> list = new ArrayList<>();
            for (int i = 0; i < 100; i++) {
                CardInfo cardInfo = new CardInfo();
                list.add(cardInfo);
            }
            return list;
        }
    }
    

    java -Xms200M -Xmx200M -XX:PrintGC HelloGC

    问题排查流程与命令

    1. JPS 查看java进程
    2. jstack定位线程状况、重点关注:WAITING BLOCKED(容易出现死锁)
    3. jinfo pid
    4. jstat -gc 4655 500 每隔500毫秒打印GC的情况
    5. jmap -histo 4655 | head -20 查看堆中的对象,如果堆内存特别大时,该命令会对进程产生影响,甚至卡顿(生成环境不适合用),压测的时候可以使用
      虚拟机启动时设定参数HeapDump, OOM的时候自动产生堆转储文件(不专业,因为有监控的话内存增长会报警)
      很多服务器备份(高可用),停掉这台服务器对其他服务器不影响
    6. jmap -dump:format=b, file=dump.bin pid
    7. 使用MAT/jhat/jvisualvm进行dump文件分析

    GC常用参数

    https://www.cnblogs.com/farmersun/p/12439969.html

    参考资料

    https://blogs.oracle.com/jonthecollector/our-collectors

  • 相关阅读:
    JAVA处理Clob大对象
    计院生活第二章 深入虎穴(上)
    我的2008
    Quartz入门到精通
    DOM4J使用教程
    JNI简介及实例
    《JavaScript凌厉开发 Ext详解与实践》3月5日开始上架销售
    计院生活第二章 深入虎穴(下)
    Access转Sql Server问题
    提高网站可用性的10个小技巧
  • 原文地址:https://www.cnblogs.com/xidongyu/p/13357861.html
Copyright © 2020-2023  润新知