场景描述

相信大家都了解 jps、jmap、jstack 等常用 java 堆栈输出命令,有过 dump、gc 分析的经验,面试中会经常被问到有关 JVM 问题,比如你是否了解你的程序在生产环境的基础配置,堆内存、栈内存怎么设置的,又是怎么估算的大小,或是垃圾回收器及回收垃圾算法的最佳使用策略。作为项目的核心开发人员,别把这些事当成是架构师要干的活,因为代码可是你一行一行码出来的,没人比你更清楚,你得负责从程序开发、黑白盒测试、项目验收、部署上线、集成交付、运维监控、用户体验等环节。越大的企业,项目模块分配的越细,这也并不代表你不需要了解整体系统的性能,其中任何一个环节出问题,都可能导致系统无法正常运行。

借由这次生产系统频繁宕机,我们总结一下 JVM 内存模型划分、JVM 启动堆内存相关参数配置及说明、各年龄代的垃圾回收器及回收过程、生产 GC 日志解读与分析、系统运行内存预估方法、启动参数如何优化等。希望通过这篇小记来和大家一起交流、一起学习。

正文

2.1 生产 GC日志文件

部分截图如下:

2.2 先看一下 jdk 1.6 的内存划分情况

按年龄划分为年轻代、老年代、永久代(方法区)、本地方法区、虚拟机栈和程序计数器。下图详细说明了这几个内存分区的关系、JVM 参数说明、存储的相关内容及各内存分区的垃圾回收器及垃圾回收算法。

2.3 生产基础环境

说明如下:

  • JDK版本:jdk_1.6
  • Web容器:Weblogic

题外话:估计市面上都是玩微服务了吧,jdk 版本至少也得 1.8 以上,jdk 1.6 不支持 G1 这么好用的垃圾收集器,也不支持 lambda 表达式,以及其他好用的特性

2.4 生产 JVM 堆内存相关参数

设置如下:

// 初始堆大小-Xms4096M// 最大堆大小-Xmx4096M// 持久代最大值-XX:MaxPermSize=1024M//......

题外话:这份配置一看就有点问题,为什么到现在才发现,因为系统之前很少出现问题,之前也未设置GC日志记录参数,也未曾关心 JVM 参数设置,大家只是在原有的工程进行开发和维护。其中 -Xmn 年轻代未配置(-XX:NewRatio 年轻代与年老代所占比值也未配置),-XX:PermSize 持久代初始值未配置(存在动态扩容带来的性能消耗)等

2.5 截取生产一条 GC 日志

图解分析如下:

2019-11-20T17:15:38.906+0800: 672725.775: [GC 2019-11-20T17:15:38.907+0800: 672725.776: [ParNew: 143735K->15199K(153344K), 0.0485240 secs] 2568043K->2439507K(4177280K), 0.0497750 secs] [Times: user=0.20 sys=0.00, real=0.05 secs] 

从以上 GC 日志文件结构图解可以清晰看出,线上生产环境的年轻代总内存大小分配约 150M,堆总内存大小约 4G,明显年轻代内存分配过小。每次 ParNew GC 老年代变化可以由堆内存大小变化和年轻代内存大小变化推算。

从下图 GC 日志可以看出,线上系统出现频繁 ParNew GC(即年轻代的 Minor GC),平均大约每 5 分钟进行一次 Minor GC,即一天平均执行 288 次之多,太可怕了吧!!!唉

题外话:为什么这么频繁,系统都线上运行3年了,当初系统上线JVM启动参数应该是随便设置的,呵呵一是系统并发量不高,二是用户量不大,三是开发人员不注重JVM优化,四是到前不久才加上GC日志输出参数,五是 pinpoint 运维监控系统居然不支持 Minor GC的监控,只支持 Full GC 监控,呵呵

2.6 CMS (Concurrent Mark Sweep)

CMS 垃圾回收器进行一次 Full GC,GC日志部分截图如下所示:

从上图可以看出,CMS 垃圾回收器正常运行(CMS 垃圾回收触发的条件:当老年代内存达到92%(3719000K / 4023936K * 100% = 92%),详情见下图)。对上图 CMS GC 进行剖析如下:

从图中可以清晰看到,CMS 对于老年代的垃圾回收分成 7 个阶段,每个阶段到底做了什么,详情见以下流程图所示:

2.7 随着用户量增加、系统并发增加

系统出现了频繁 Full GC,pinpoint 监控内存使用情况如下(只能监控老年代的 Full GC,而无法监控年轻代的 Minor GC,其实 Full GC 之前 Minor GC 执行次数频率更可怕):

2.8 ParNew + CMS 组合

ParNew(年轻代垃圾回收器) + CMS(老年代垃圾回收器) 回收器组合是在 JDK 1.8 之前大多数 JAVA 企业级服务应用的最佳选择,从以下生产 GC 日志截图中可以看到,在 CMS 回收器触发时,出现了 promotion failed 和 concurrent mode failure 现象:

针对这两个现象产生的原因进行解读如下:

  • promotion failed该现象是在进行触发年轻代 ParNew GC 时,存活的对象在 Survivor 区放不下,对象只能进入老年代,而此时老年代也放不下导致的。
  • concurrent mode failure该现象是在执行 CMS 回收器回收垃圾的过程中同时有存活的对象放入老年代,而此时老年代空间不足,或者在做 ParNew GC 的时候,年轻代 Survivor 区放不下,需要放入老年代,而老年代也放不下而导致的。

2.9 解决方案

针对以上2种现象产生的原因进行 JVM 相关参数优化:

可增大年轻代或者 Survivor 区的存储空间

-Xmn1500M-XX:SurvivorRatio=8

或者提前触发 CMS 垃圾回收和进行 5 次 CMS 垃圾回收后整理清除碎片

-XX:+UseCMSCompactAtFullCollection-XX:CMSFullGCsBeforeCompaction=5-XX:+UseCMSInitiatingOccupancyOnly-XX:CMSInitiatingOccupancyFraction=80

2.10 最后对生产环境的 JVM 内存参数设置进行优化

建议虚拟机参数设置如下:

-Xms4096M-Xmx4096M-Xmn1500M-XX:PermSize=1024M-XX:MaxPermSize=1024M-Xss512K-XX:SurvivorRatio=8-XX:+UseConcMarkSweepGC-XX:+UseParNewGC-XX:+CMSParallelRemarkEnabled-XX:+UseCMSCompactAtFullCollection-XX:CMSFullGCsBeforeCompaction=5-XX:+UseCMSInitiatingOccupancyOnly-XX:CMSInitiatingOccupancyFraction=80-XX:+PrintGCDetails-XX:+PrintGCTimeStamps-Xloggc:log/gc.log

线上系统内存估算方法

3.1 Java对象属性类型所占字节大小

列表清单如下:

3.2 Java对象所占JVM内存结构

如下图展示:

可以看到数组类型对象和普通对象的区别仅在于 4 字节数组长度的存储区间。而对象指针究竟是 4 字节还是 8 字节要看是否开启指针压缩。Oracle JDK 从 6_update_23 开始在 64 位系统上会默认开启压缩指针。如果要强行关闭指针压缩使用 -XX:-UseCompressedOops,强行启用指针压缩使用:-XX:+UseCompressedOops。

假如生产订单某一对象大约30字段,如订单对象 JavaBeanA ,所占内存大小计算的方法如下所示:

public class ObjectA {          int a;       // 4 Byte         byte b;      // 1 Byte        String c;   // 4 Byte        double d;       // 8 Byte        String e;   // 4 Byte        // 此处省略25个String对象 25*4 Byte        ObjectB objB;  // 8 Byte }public class ObjectB {      // ...    }
Size(ObjectA) = Size(对象头(_mark)) + size(oop指针) + size(数据区)Size(ObjectA) = 8 + 4 + 4(int) + 1(byte) + 4(String) * 26 + 8(double) + 7(padding) + 8(ObjectB指针)Size(ObjectA) = 136 字节 = 136 / 1024 kb = 0.133 kb

由此,可以大约估算出你的线上系统每秒产生多少 M 的对象。如果每秒产生 500 个 ObjectA,即大约 0.5 M,那么对于年轻代 1500M 的内存,大约需要 3000s 充满,即 50 min才触发一次 Minor GC,也就是说一天大约触发24次 Minor GC

总结

  • 对于生产系统,合理增大年轻代内存大小,本着尽量减少系统 Minor GC,一日最多一次 Full GC 的原则;
  • 优化编码,减少不必要的对象创建,合理定义对象,合理使用和优化数据结构;
  • 优化 JVM 内存参数以减少 GC 次数,生产选择换最优垃圾收集器配置策略。

gc日志一般关注什么_记一次生产频繁出现 Full GC 的 GC日志图文详解相关推荐

  1. mysql 集群切换_完美起航-MySQLMHA高可用集群部署及故障切换(图文详解)

    MySQL MHA 一.MHA概念 MHA(MasterHigh Availability)是一套优秀的MySQL高可用环境下故障切换和主从复制的软件. MHA 的出现就是解决MySQL 单点的问题. ...

  2. 从python存入的文件是乱码_如何解决python写入html文件中乱码的现象(图文详解)...

    python写入html文件中文乱码问题 使用open函数将爬虫爬取的html写入文件,有时候在控制台不会乱码,但是写入文件的html中的中文是乱码的 案例分析 看下面一段代码:# 爬虫未使用cook ...

  3. 目录树 删除 数据结构_数据结构:B树和B+树的插入、删除图文详解

    B树 1.1B树的定义 B树也称B-树,它是一颗多路平衡查找树.我们描述一颗B树时需要指定它的阶数,阶数表示了一个结点最多有多少个孩子结点,一般用字母m表示阶数.当m取2时,就是我们常见的二叉搜索树. ...

  4. 确定sw1开关信号输入端口_三菱PLC入门 | FX2N系列PLC的信号输入端子接线(图文详解)...

    输入端口在FX2N的上部,下图是FX2N-48MR PLC的输入端口图. 关于电源输入和直流24V输出.我们在上面已介绍过,这里不在叙述. FX2N的输入端口统一称为输入继电器X,其地址(就是端口的编 ...

  5. 联想台式主机拆机教程_联想z465怎么拆机 联想z465拆机教程【图文详解】

    我们现在很多人都在用手提电脑,而且越来越多的人喜欢用手提电脑.因为相比较于传统的台式电脑,手提电脑更加轻便,易于携带.所以,现在许多人出差或者去其他的地方参加活动都会携带手提电脑.但是,我们都知道手提 ...

  6. 怎么把ppt弄成链接的形式_怎样制作ppt课件 如何将ppt转换成视频【图文详解】...

    怎样制作ppt课件? Powerpoint做出来的东西叫演示文稿,它是一个文件,其格式后缀名为.ppt,或者也可以保存为.pdf.图片格式等,2010版本中可保存为视频格式.近年来,中国的PPT应用水 ...

  7. ebay注册流程_【eBay新手开店】2020年eBay注册开店流程图文详解

    今天跟大家分享最新的eBay注册开店流程,希望对卖家小伙伴们有所帮助.我们先简单了解一下eBay注册条件. 企业注册eBay需满足以下条件 ·合法登记的企业用户,并且能提供eBay要求的所有相关文件 ...

  8. 【图文详解】一文全面彻底搞懂HBase、LevelDB、RocksDB等NoSQL背后的存储原理:LSM-tree日志结构合并树...

    LSM 树广泛用于数据存储,例如 RocksDB.Apache AsterixDB.Bigtable.HBase.LevelDB.Apache Accumulo.SQLite4.Tarantool.W ...

  9. 希云Docker培训视频百度云_希云cSphere-最佳实战Docker持续集成图文详解

    前言 关于Docker的文章铺天盖地,但精品文章往往翻译居多.都说Docker天生适合持续集成/持续部署,但同样,可落地.实际可操作性的文章也很罕见. 基于这些情况,虽然我们专栏定位为运维管理性文字, ...

最新文章

  1. bootsrap学习
  2. 报名 | 加密金融生态大咖私享会
  3. python view函数_Python爬虫实例(二)——爬取新馆疫情每日新增人数
  4. python队列来做什么_python分布式爬虫中的消息队列是什么?
  5. 企业微信与微信互通能力再升级 全面打通与视频号的连接
  6. 三星Galaxy S22系列曝光:首批搭载高通骁龙895!
  7. MS DOS窗口进入JAVA源程序,从java程序运行MS-DOS命令
  8. ROBOTS.TXT在SEO优化中的运用(ROBOTS.TXT SEO优化实战)
  9. verilog实现多周期处理器之——(五)移动操作(通用数据传送)指令的实现
  10. Thread源码-----传实现了Runnable接口的类的实例给Thread的作用
  11. SAP - MM - 第3篇 - 供应商主数据
  12. 金三银四跳槽季,教你这几招提高面试成功率
  13. DSPE-PEG-TAT,磷脂-聚乙二醇-靶向穿膜肽TAT,一种磷脂PEG肽
  14. 分析网易云用户运营的指标监控和召回机制
  15. 突发!又一MCU大厂暂停接单!
  16. mysql存储函数中游标报错 No data - zero rows fetched, selected
  17. 只能替换有源晶振 时钟发生器_有源晶振选型与替换原则
  18. Sourcetree安装详细(最新版本)
  19. 使用全局变量有什么好处?有什么坏处?_一起来了解下:喝贡菊花茶有什么好处、喝菊花茶的好处和坏处...
  20. 从零开始的Java笔记01

热门文章

  1. python 爬虫 scrapy 和 requsts 哪个快_Scrapy爬虫框架结构以及和Requests库的比较
  2. LCS(最长公共子串)系列问题
  3. asyncio简单入门(二)
  4. linux mysql运维_Linux运维常用的 MySQL基础命令
  5. Myeclipse破解总结
  6. Oracle和sql server中复制表结构和表数据的sql语句
  7. VB.NET网络是否联通Function
  8. 关于android ksoap获取失败的问题
  9. JAVA中堆栈和内存分配原理
  10. 【转】gdb调试多进程程序