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

posted @ 2020-07-21 22:34  被罚站的树  阅读(449)  评论(0编辑  收藏  举报