今天线上的hadoop集群崩溃了,现象是namenode一直在GC,长时间无法正常服务。最后运维大神各种倒腾内存,GC稳定后,服务正常。虽说全程在打酱油,但是也跟着学习不少的东西。

第一个问题:为什么会频繁GC

有过JVM经验的开发者都应该知道,GC是在内存不够时,JVM自动进行的自我救赎(删除不用的数据,释放内存空间)。那么NameNode在什么情况下会进行GC呢?在解释这个问题之前,需要明白GC的几种级别,以及触发的条件:

  • Minor GC:清理新生代,一般都是复制回收算法
  • Full GC:清理所有的内存,新生代、老年代、元空间、堆外内存...(Major GC清理老年代,一般采用标记清除/标记整理)

Full GC的触发条件一般是:

  1. System.gc()
  2. jmap -histo:live pid
  3. minor gc时检查年老代的空间是否够年轻代用
  4. 使用大对象
  5. 长期持有引用

参考:https://blog.csdn.net/chenleixing/article/details/46706039

可以通过下面的配置输出GC日志,并记录FullGC前的线程情况:

-Xmx2g -XX:+HeapDumpBeforeFullGC
-XX:HeapDumpPath=. -Xloggc:gc.log
-XX:+PrintGC -XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFiles=10
-XX:GCLogFileSize=100m
-XX:HeapDumpOnOutOfMemoryError

再来说说,namenode什么情况下会触发GC:

由于namenode中需要维护namespace(目录结构)和blockmanager(block状态),因此内存消耗很严重。如果小文件很多,势必会增加两部分的内存消耗。

参考:https://blog.csdn.net/qq_25418883/article/details/79926153

第二个问题:如何查看线上进程的GC情况

JVM提供了很多JVM内存分析的工具,如jstat,使用的方法就是:

# 先执行top查找指定的进程pid
top
# 然后使用jstat分析内存情况
jstat -gc 12345 5000
# 12345是java的进程id,5000是gc的间隔时间,单位是ms

我这边有一段gc的记录:

[hdfs@hnode1 ~]$ jstat -gc 1842 5000
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
127744.0 127744.0 0.0 116918.1 1022464.0 755982.7 11304960.0 1199292.3 30752.0 30014.9 3672.0 3477.0 25 3.058 2 0.302 3.360
127744.0 127744.0 0.0 127744.0 1022464.0 0.0 11304960.0 1643454.1 30752.0 30150.5 3672.0 3492.1 35 4.111 2 0.302 4.413
127744.0 127744.0 127744.0 0.0 1022464.0 613512.1 11304960.0 2042124.0 30752.0 30161.0 3672.0 3493.6 44 5.336 2 0.302 5.638
127744.0 127744.0 127744.0 122761.9 1022464.0 1022464.0 11304960.0 2517047.0 30752.0 30161.0 3672.0 3493.6 55 6.121 2 0.302 6.422
127744.0 127744.0 0.0 127744.0 1022464.0 304352.9 11304960.0 2976155.9 31008.0 30286.0 3672.0 3501.9 65 6.950 2 0.302 7.252
127744.0 127744.0 0.0 127744.0 1022464.0 211192.9 11304960.0 3267759.0 31008.0 30498.1 3672.0 3525.3 71 7.498 2 0.302 7.800
127744.0 127744.0 127744.0 0.0 1022464.0 467990.3 11304960.0 3413694.4 31008.0 30498.1 3672.0 3525.3 72 7.834 2 0.302 8.136
127744.0 127744.0 0.0 127744.0 1022464.0 693620.3 11304960.0 3598033.7 31008.0 30502.2 3672.0 3525.3 73 8.333 2 0.302 8.635
127744.0 127744.0 0.0 67244.8 1022464.0 245206.5 11304960.0 3953531.0 31264.0 30745.0 3672.0 3547.3 77 9.108 2 0.302 9.410
127744.0 127744.0 111122.2 0.0 1022464.0 736237.5 11304960.0 4239210.9 31264.0 30746.5 3672.0 3547.3 88 9.792 2 0.302 10.094
127744.0 127744.0 119642.4 0.0 1022464.0 470375.6 11304960.0 4575876.0 31264.0 30746.5 3672.0 3547.3 100 10.584 2 0.302 10.886
127744.0 127744.0 0.0 127672.5 1022464.0 878275.8 11304960.0 4887903.6 31264.0 30767.6 3672.0 3547.3 111 11.314 2 0.302 11.616

在GC日志中能看到什么?

1 各项指标的含义

  • S0C:第一个幸存区的大小

  • S1C:第二个幸存区的大小

  • S0U:第一个幸存区的使用大小

  • S1U:第二个幸存区的使用大小

  • EC:伊甸园区的大小(新生代)

  • EU:伊甸园区的使用大小

  • OC:老年代大小

  • OU:老年代使用大小

  • MC:方法区大小

  • MU:方法区使用大小

  • CCSC:压缩类空间大小

  • CCSU:压缩类空间使用大小

  • YGC:年轻代垃圾回收次数

  • YGCT:年轻代垃圾回收消耗时间

  • FGC:老年代垃圾回收次数

  • FGCT:老年代垃圾回收消耗时间

  • GCT:垃圾回收消耗总时间

  • 参考:https://blog.csdn.net/zlzlei/article/details/46471627

  • 参考:https://blog.csdn.net/maosijunzi/article/details/46049117

2 新生代的垃圾回收模式

由于JVM设计者认为,大部分的对象都是新创建的,生命周期都不长。因此新建的对象会直接放在新生代中,并采用复制回收机制。即保证to区总是空的,每次触发GC的时候,就会把Eden和from survivor中的还存活的对象拷贝到to区中。然后to变成了from,from变成to。这样反复几次,还存活的对象,就会拷贝到old老年代当中。

在配置JVM的时候就有几个比较重要的参数:

  • -Xms 和 -Xmx 配置了堆的最小和最大内存
  • -XX:NewSize 和 -XX:MaxNewSize 配置了新生代的内存。最大是Xmx的一半,不过最好还是看业务场景
  • -XX:NewRatio 设置新生代和老年代的比例,如 -XX:NewRatio=3 指定老年代/新生代为3/1
  • -XX:SurvivorRatio 设置survivor与eden的比例,如 -XX:SurvivorRatio=10 表示eden是survivor的10倍,即survivor每个占1/12,eden占10/12
  • -XX:InitialTenuringThreshold, -XX:MaxTenuringThreshold and -XX:TargetSurvivorRatio 控制进入老年代的条件,例如 , -XX:MaxTenuringThreshold=10 -XX:TargetSurvivorRatio=90 设定老年代阀值的上限为10,幸存区空间目标使用率为90%
  • -XX:+NeverTenure and -XX:+AlwaysTenure 分别表示永远不进入老年代,和一次GC存活就进入老年代

参考:http://ifeve.com/useful-jvm-flags-part-5-young-generation-garbage-collection/

YGC和FGC

通过前面的讲述,应该了解到:

1 频繁的YGC

表示新创建的对象过多、survivor剩余空间太小;如果老年代内存利用率不高,那么可以考虑调整老年代的阈值。

2 频繁的FGC

就需要认真排查了,比如:

  • 查看堆空间大小分配(年轻代、年老代、持久代分配)
  • 垃圾回收监控(长时间监控回收情况)
  • 线程信息监控:系统线程数量
  • 线程状态监控:各个线程都处在什么样的状态下
  • 线程详细信息:查看线程内部运行情况,死锁检查
  • CPU热点:检查系统哪些方法占用了大量CPU时间
  • 内存热点:检查哪些对象在系统中数量最大

看看是不是内存发生泄漏,内存泄漏的情况:

  1. 静态集合类。生命周期会跟类的生命周期一样,因此不会被GC
  2. 集合里面对象的属性改变。比如HashSet,改变了某个对象的属性,导致hashcode发生变化。但是引用还在。
  3. 控件的监听器
  4. 各种连接,没有close
  5. 外部模块的引用
  6. 单例模式,如果它持有外部对象的引用。

4 PC和PU去哪里了

在老版本的JVM中,类的方法数据、字节码、变量大小、常量池等等被认为是静态信息,不由程序创建而控制,存储在永久带Perm当中。

在1.8中,这部分数据放入了一个叫做元空间的地方。

带来的好处:

  • 元空间使用系统内存,不会出现内存溢出(不过内存泄漏一样会占用系统内存哦)
  • 元空间不由垃圾回收器管理,之前垃圾回收效率也很低

参考:https://www.cnblogs.com/paddix/p/5309550.html

可以优化的点

1 合并小文件

2 优化目录树:https://www.cnblogs.com/yangjiandan/p/3535125.html

记一次线上事故的JVM内存学习的更多相关文章

  1. 由定时脚本错误以及Elasticsearch配置错误引发的Flink线上事故

    近期接手离职同事项目,突然遇到线上事故,Flink无法正常聚合数据生成指标. 以下是详细的排查过程: 问题复现 清晨,运维报告Flink数据分析模块无法正常生成指标数据. 赶紧登陆Flink所在机器, ...

  2. 记一次线上bug排查-quartz线程调度相关

    记一次线上bug排查,与各位共同探讨. 概述:使用quartz做的定时任务,正式生产环境有个任务延迟了1小时之久才触发.在这一小时里各种排查找不出问题,直到延迟时间结束了,该任务才珊珊触发.原因主要就 ...

  3. 解Bug之路-记一次线上请求偶尔变慢的排查

    解Bug之路-记一次线上请求偶尔变慢的排查 前言 最近解决了个比较棘手的问题,由于排查过程挺有意思,于是就以此为素材写出了本篇文章. Bug现场 这是一个偶发的性能问题.在每天几百万比交易请求中,平均 ...

  4. 记一次线上OOM问题分析与解决

    一.问题情况 最近用户反映系统响应越来越慢,而且不是偶发性的慢.根据后台日志,可以看到系统已经有oom现象. 根据jdk自带的jconsole工具,可以监视到系统处于堵塞时期.cup占满,活动线程数持 ...

  5. 记一次线上coredump事故

    1.事故背景 上周三凌晨,我负责的某个模块在多台机器上连续发生coredump,幸好发生在业务低峰期,而且该模块提供的功能也不是核心流程功能,所以对线上业务影响比较小.发生coredump后,运维收到 ...

  6. 记一次线上Curator使用过程JVM栈溢出解决

       为了同学们看起来一目了,特按如下思路进行讲解. 1.出现的场景    2.分析及解决的过程    3.总结 最近公司要使用zookeeper做配置管理(后面简称ZK),然后自己就提前用虚拟机进行 ...

  7. 记一次真实的线上事故:一个update引发的惨案!

    目录 前言 项目背景介绍 要命的update 结语 前言   从事互联网开发这几年,参与了许多项目的架构分析,数据库设计,改过的bug不计其数,写过的sql数以万计,从未出现重大纰漏,但常在河边走,哪 ...

  8. 【MySQL】记一次线上重大事故:二狗子竟然把线上数据库删了!!

    写在前面 估计二狗子这几天是大姨夫来了,心情很郁闷,情绪也很低落,工作的时候也有点心不在焉.让他发个版本,结果,一行命令下去把线上的数据库删了!你没听错:是删掉了线上的数据库!运营那边顿时炸了锅:怎么 ...

  9. 记一次线上gc调优的过程

           近期公司运营同学经常表示线上我们一个后台管理系统运行特别慢,而且经常出现504超时的情况.对于这种情况我们本能的认为可能是代码有性能问题,可能有死循环或者是数据库调用次数过多导致接口运行 ...

随机推荐

  1. oracle 中删除表 drop delete truncate

    oracle 中删除表 drop delete truncate   相同点,使用drop delete truncate 都会删除表中的内容 drop table 表名 delete from 表名 ...

  2. [ExecuteInEditMode]

    ExecuteInEditMode属性的作用是在EditMode下也可以执行脚本.Unity中默认情况下,脚本只有在运行的时候才被执行,加上此属性后,不运行程序,也能执行脚本. 与PlayMode不同 ...

  3. 2019.01.21 bzoj2989: 数列(二进制分组+主席树)

    传送门 二进制分组入门题. 主席树写错调题2h+2h+2h+体验极差. 题意简述:给一堆点,支持加入一个点,询问有多少个点跟(x,y)(x,y)(x,y)曼哈顿距离不超过kkk. 思路:题目要求的是对 ...

  4. IntelliJ IDEA 2017版 spring-boot2.0.2 搭建 JPA springboot DataSource JPA 实体类浅谈

    一.实体类分析 一般用到的实体类的类型有 String类型.Long类型.Integer类型.Double类型.Date类型.DateTime类型.Text类型.Boolean型等 1.String类 ...

  5. 安卓 build/core/Makefile 以及main.mk

    android make 系统总共分为四层 arch board device product 在各个字android.mk文件中引用的定义都存放在./build/core/下!比如android.m ...

  6. WordPaster-KesionCMS V8整合教程

    1.上传WordPaster文件夹 2.上传ckeditor3x插件文件夹 4.修改ckeditor编辑器的config.js文件,启用插件,在工具栏中增加插件按钮 5.在文章页面增加插件初始化代码 ...

  7. _编程语言_C++_简介

    扩展名: .cpp..cp或.c C++编译器: GNU的gcc 编译器

  8. java如何编写下载功能

    @RequestMapping("/downLoadFailRecord") public ModelAndView downLoadFailRecord( HttpServlet ...

  9. oj错误之char型超出范围

    在oj时遇到一个题 题目本身并不是很难,但在一个数据时出了错,刚开始一直没想通是哪里出了错 下面为源代码 #include <bits/stdc++.h> using namespace ...

  10. codeblock快捷键使用

    •在编辑区按住右键可拖动代码,省去拉滚动条之麻烦:相关设置:Mouse Drag Scrolling. •Ctrl+D可复制当前行或选中块. •可拖动选中块使其移动到新位置,按住Ctrl则为复制到新位 ...