今天线上的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. 【Ruby】ruby安装

    Ruby简介 Ruby,一种简单快捷的面向对象(面向对象程序设计)脚本语言,在20世纪90年代由日本人松本行弘(Yukihiro Matsumoto)开发,遵守GPL协议和Ruby License.它 ...

  2. 13个开源GIS软件 你了解几个?

    地理信息系统(Geographic Information System,GIS)软件依赖于覆盖整个地球的数据集.为处理大量的 GIS 数据及其格式,编程人员创建了若干开源库和 GIS 套件. GIS ...

  3. hdu-1043(八数码+bfs打表+康托展开)

    参考文章:https://www.cnblogs.com/Inkblots/p/4846948.html 康托展开:https://blog.csdn.net/wbin233/article/deta ...

  4. App测试基本流程详解(汇总整理)

    前言 看过许多大神对APP测试的理解,博主总结了一下我们平时测试APP应该注意的一些测试点并结合大神的理解,总结出这篇文章. 一.测试周期 测试周期一般为两周,根据项目情况以及版本质量可适当缩短或延长 ...

  5. VB6.0中WinSock控件属性和方法详解

    原文链接:http://liweibird.blog.51cto.com/631764/653134 WinSock控件能够通过UDP协议(用户数据报协议)或TCP协议(数据传输协议)连接到远程的机器 ...

  6. Python 之 Difflib

    Python 之 Difflib 2017年7月8日 word文档地址:https://wenku.baidu.com/view/36692440854769eae009581b6bd97f19237 ...

  7. ★ prototype、__proto__ 详解

    # var Person = function(name) { this.name = name; } var p = new Person(); //new操作符的操作是 var p = {} p. ...

  8. $.contains(a,b)

    jQuery.contains()函数用于判断指定元素内是否包含另一个元素. 简而言之,该函数用于判断另一个DOM元素是否是指定DOM元素的后代. 该函数属于全局jQuery对象. 语法 jQuery ...

  9. C++之引用和指针

    作者:tongqingliu 转载请注明出处:http://www.cnblogs.com/liutongqing/p/7050431.html C++之引用和指针 C++引用 引用的基本用法: in ...

  10. day27(反射之内省机制)

    内省 内省:底层是使用反射机制实现的,是对于反射的进一步封装. 反射:通过类名来获取类中的所有属性和方法及类中的所有隐藏的方法. 内省:通过一个标准类(javabean类)来获取bean中的字段.ge ...