背景:
 
问题:
有个渠道支付服务,负责与所有支付相关服务进行交互,包括 渠道下单支付,渠道成功通知,渠道的对账等
服务4台机,平时跑的都很稳定,通过thrift进行对外提供服务,且平时并未发现访问超时的情况,已经平稳在线上跑了1年多了,没出现过超时问题。
但最近发现,每天到了晚上凌晨2点开始大量服务访问超时,且定位到每次都是抢到对账任务的那台服务出现问题。
 
解决:
后通过监控和打印GC日志发现,出现问题机器服务的Major GC频率增加,应该是内存问题。
故把对账程序拆出后单独部署后, 再没出现服务访问超时情况。
 
分析,对账时,因为要捞出当天支付数据到内存进行对账(随着业务发展订单开始猛增)故触发了GC。其实增加分批limit当然也可以解决
这是最近第二次踩到GC的坑了, 第一次是每次访问给每个线程分配的内存过多,并发上来后性能严重降低,导致WEB超时,有必要总结下了
 
 
 
重现下:
 
死磕重现下,
 
模拟业务部分,假使每个业务需要500ms返回,代码如下:
/**
* 模拟当系统中使用大对象时,对JVM造成的影响
*
* @author 包子(何锦彬). 2017.01.07
* @QQ 277803242
*/
@WebServlet("/Test")
public class Test extends HttpServlet {
private static final long serialVersionUID = 1L;
private Logger logger = Logger.getLogger(Test.class.getName()); protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
// deal
try {
// 模拟业务花费时间
Thread.sleep(500);
} catch (InterruptedException e) {
}
long costTime = (System.currentTimeMillis() - startTime);
// 接口1秒timeout,打印出日志
if (costTime > 1000) {
logger.warning("cost time:" + costTime);
}
Writer out = response.getWriter();
out.append("ok");
}
}
 
放入tomcat,加入垃圾回收方式和堆内存大小,打印垃圾回收日志
 
JAVA_OPTS="-Xmx1200m -Xms1200m -Xmn200m -Xss228k -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+PrintGCDetails"
 
用jmeter压测1分钟后,发现正常,每次返回均在500毫秒左右,日志里也没有出现访问超时。
 
 
 
加入模拟对账部分代码,1分钟后对账开启,持续提供服务,为了让它触发GC,模拟加载了1G的待对账数据,(当然,在生产环境里只是一个大对象,但OU区本来就有东西了)
 
/**
* 模拟定时对账, 模拟来了个大对象对账
*
* @author 包子(何锦彬). 2017.01.07
* @QQ 277803242
*/
@WebServlet(name = "init", loadOnStartup = 1, description = "init", urlPatterns = "/task")
public class BigObject extends HttpServlet {
class ReconciliationTask extends TimerTask { @Override
public void run() {
logger.warning("a big object have commit,exp: reconciliation");
byte[] bigObject = new byte[1024 * 1024 * 500];// 年老代 500M,刚刚好达到年老代的50%,不断触发GC
try {
Thread.sleep(60 * 1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} } private static final long serialVersionUID = 1L;
private Logger logger = Logger.getLogger(Test.class.getName()); @Override
public void init() throws ServletException {
Timer timer = new Timer();
timer.schedule(new ReconciliationTask(), 60 * 1000);// 1分钟后模拟开始对账任务
logger.log(Level.INFO, "task have registered");
super.init();
}
}
 
和上面一样的参数启动tomcat, 1G内存,CMS回收,用jmeter压测1分钟后,发现 Major GC大量发生,截取一周期的Major GC日志如下:
 
GC [1 CMS-initial-mark: 1048576K(1740800K)] 1051853K(1832960K), 0.0042630 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[CMS-concurrent-mark: 0.023/0.023 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
[CMS-concurrent-preclean: 0.007/0.007 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
CMS: abort preclean due to time [CMS-concurrent-abortable-preclean: 0.222/5.020 secs] [Times: user=0.20 sys=0.03, real=5.02 secs]
[GC[YG occupancy: 3277 K (92160 K)][Rescan (parallel) , 0.0067540 secs][weak refs processing, 0.0000400 secs][scrub string table, 0.0004160 secs] [1 CMS-remark: 1048576K(1740800K)] 1051853K(1832960K), 0.0076820 secs] [Times: user=0.02 sys=0.01, real=0.01 secs]
[CMS-concurrent-sweep: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-reset: 0.014/0.014 secs] [Times: user=0.00 sys=0.01, real=0.01 secs]
此时的用jstat查看的话情况如下:
 
S0C      S1C     S0U    S1U      EC       EU        OC         OU       PC     PU    YGC    YGCT    FGC    FGCT     GCT 
20480.0 20480.0 0.0 17257.9 163840.0 93241.7 1024000.0 512000.0 29356.0 17612.0 1 0.032 5 0.159 0.191
20480.0 20480.0 0.0 17257.9 163840.0 93241.8 1024000.0 512000.0 29356.0 17612.0 1 0.032 5 0.159 0.191
20480.0 20480.0 0.0 17257.9 163840.0 93241.9 1024000.0 512000.0 29356.0 17612.0 1 0.032 5 0.159 0.191
20480.0 20480.0 0.0 17257.9 163840.0 93242.0 1024000.0 512000.0 29356.0 17612.0 1 0.032 5 0.159 0.191
20480.0 20480.0 0.0 17257.9 163840.0 93242.0 1024000.0 512000.0 29356.0 17612.0 1 0.032 5 0.159 0.191
20480.0 20480.0 0.0 17257.9 163840.0 93242.1 1024000.0 512000.0 29356.0 17612.0 1 0.032 6 0.183 0.214
 
导致结果,大量接口访问超时,截取一部分日志如下:
13-Jan-2017 01:26:43.692 WARNING [http-nio-8080-exec-78] org.hjb.memory.Test.doGet cost time:1482
13-Jan-2017 01:26:43.693 WARNING [http-nio-8080-exec-28] org.hjb.memory.Test.doGet cost time:1434
13-Jan-2017 01:26:43.694 WARNING [http-nio-8080-exec-23] org.hjb.memory.Test.doGet cost time:1492
13-Jan-2017 01:26:43.699 WARNING [http-nio-8080-exec-85] org.hjb.memory.Test.doGet cost time:1514
13-Jan-2017 01:26:43.700 WARNING [http-nio-8080-exec-97] org.hjb.memory.Test.doGet cost time:1515
13-Jan-2017 01:26:43.701 WARNING [http-nio-8080-exec-83] org.hjb.memory.Test.doGet cost time:1517
13-Jan-2017 01:26:43.704 WARNING [http-nio-8080-exec-87] org.hjb.memory.Test.doGet cost time:1547
13-Jan-2017 01:26:43.705 WARNING [http-nio-8080-exec-41] org.hjb.memory.Test.doGet cost time:1548
13-Jan-2017 01:26:43.707 WARNING [http-nio-8080-exec-35] org.hjb.memory.Test.doGet cost time:1552
13-Jan-2017 01:26:43.707 WARNING [http-nio-8080-exec-26] org.hjb.memory.Test.doGet cost time:1557
13-Jan-2017 01:26:43.709 WARNING [http-nio-8080-exec-101] org.hjb.memory.Test.doGet cost time:1559
13-Jan-2017 01:26:43.690 WARNING [http-nio-8080-exec-42] org.hjb.memory.Test.doGet cost time:1482
13-Jan-2017 01:26:43.716 WARNING [http-nio-8080-exec-80] org.hjb.memory.Test.doGet cost time:1578
13-Jan-2017 01:26:43.720 WARNING [http-nio-8080-exec-3] org.hjb.memory.Test.doGet cost time:1583
13-Jan-2017 01:26:43.690 WARNING [http-nio-8080-exec-22] org.hjb.memory.Test.doGet cost time:1420
13-Jan-2017 01:26:43.690 WARNING [http-nio-8080-exec-59] org.hjb.memory.Test.doGet cost time:1295
13-Jan-2017 01:26:43.689 WARNING [http-nio-8080-exec-51] org.hjb.memory.Test.doGet cost time:1294
13-Jan-2017 01:26:43.727 WARNING [http-nio-8080-exec-79] org.hjb.memory.Test.doGet cost time:1559
13-Jan-2017 01:26:43.731 WARNING [http-nio-8080-exec-63] org.hjb.memory.Test.doGet cost time:1630
13-Jan-2017 01:26:43.687 WARNING [http-nio-8080-exec-77] org.hjb.memory.Test.doGet cost time:1429
13-Jan-2017 01:26:43.686 WARNING [http-nio-8080-exec-95] org.hjb.memory.Test.doGet cost time:1371
13-Jan-2017 01:26:43.737 WARNING [http-nio-8080-exec-8] org.hjb.memory.Test.doGet cost time:1595
13-Jan-2017 01:26:43.686 WARNING [http-nio-8080-exec-84] org.hjb.memory.Test.doGet cost time:1429
13-Jan-2017 01:26:43.686 WARNING [http-nio-8080-exec-92] org.hjb.memory.Test.doGet cost time:1295
13-Jan-2017 01:26:43.678 WARNING [http-nio-8080-exec-20] org.hjb.memory.Test.doGet cost time:1390
上面是HTTP的,thrift访问其实也是一样的。
 
 
 
总结:
 
大对象使用会严重影响到服务的性能质量,时刻观察GC的发生。
 
 

踩过GC的几个坑

1, 内存不够用,JVM用到SWAP

如果每次GC时间不合理时,如: FGC=10,FGCT=1, 基本可以肯定是用到了SWAP内存区了 (当然也和你内存大小有关, 指一般系统内存在8G附近)

2, 用到大对象,导致频繁FGC( FULLGC和 Major GC 对性能都有严重影响)

看很多资料上说是60%触发Major GC, 但经测试发现, 在OU到了50%时开始不断触发Major GC

4,YGC变动越来越久, 如用到了string.intern()方法. (比如在 fastjson 中有用到), 

原因: string.intern() 往常量池加如数据。 而ROOT GC时, 需要扫描所有常量作为根节点, 当常量池越大量增加时,扫描的数据时间增加

5,GC里面出现有

Concurrent mode failed

原因:由于GC在CMS时,往OU区放入对象,然后不可用。

发现大量这种现象,

1,增加OLD区的内存压缩参数, UseCMSCompactAtFullCollection 或 CMSFullGCsBeforeCompaction

2, 建议调大年轻代内存调大,或增加OLD区的回收频率

Prommotion failed

1. 年轻代对象晋升失败, 当启用了担保分配,每次晋升会检查年老代的内存是否够大于年轻代平均晋升对象的大小,如果小于将会进行FULLGC,日志会显示Prommotion faile引起fullGC

2, JDK6后默认都开启了担保分配

再解释下GC的几个名词

有几个误区:

FULL GC , Major GC , Minor GC

Minor GC 就是年轻代清理,这个好理解

FULL GC 指整个永久代(或G1的Metaspace代)的和堆内存 ,堆外内存一起清理.  在GC日志里的FULL GC指的是这个

Major GC 年老代的清理, 在jstat -gc 里的FGC只是指这个,只是年老代的GC而已

详细:https://plumbr.eu/handbook/garbage-collection-in-java

误区:jstat 的FGC, 它统计的仅仅是STW的次数,即 两个init-mark和 remark的阶段

问题:

引起FULL GC的原因有哪些呢?

1、System.gc()方法的调用 
2、老年代代空间不足 
3、永生区空间不足 
4、CMS GC时出现,如GC日志中找到promotion failed(晋升失败)和concurrent mode failure(回收时有对象要分配) 
5、统计得到的Minor GC晋升到旧生代的平均大小大于老年代的剩余空间 
6、空间碎片太多,堆中分配很大的对象(如果如此,建议每次FULLGC后开启压缩)

如何查看GC 发生的原因呢?

目前能想到的,最简单是 jstat -gccause

1,这种跑批的任务不应该和服务放一块后面,应该是单独的模块

2. 对于对账,我的解决是后面放mongoDB,然后拿出来进行对账,完全是个离线处理的过程了

3,感谢各位的疑问。其实不一定是对账,很多系统会把跑批和服务放一块,如果跑批涉及到把数据load到内存的话,一定要注意垃圾回收了。不然容易触发CMS的GC。 而且经过我测试,每次到了老年代的50%就会不断触发GC,如果跑批任务持续时间比较久,不释放对象,将不停的Major GC。

有个疑问

上面例子中, 我加上 +XX:CMSInitiatingOccupancyFraction=80 后,还是会不停的Major GC( 例子中只占50%),待解答

持续更新留言问题,解答疑问

欢迎关注我的公众号,专注重现各种线上的BUG

或搜 “包子的实验室”

 

一个大对象引起的血案,GC的踩坑实录的更多相关文章

  1. java动态返回一个大对象里多个小对象map返回,el表达式用c:set拼接

    基于堆内存,先把map放到返回值里 Map _map=new HashMap(); modelAndView.addObject("pledgeInsurance",_map);/ ...

  2. .Net 垃圾回收和大对象处理

    CLR垃圾回收器根据所占空间大小划分对象.大对象和小对象的处理方式有很大区别.比如内存碎片整理 —— 在内存中移动大对象的成本是昂贵的,让我们研究一下垃圾回收器是如何处理大对象的,大对象对程序性能有哪 ...

  3. .Net 垃圾回收和大对象处理 内存碎片整理

    CLR垃圾回收器根据所占空间大小划分对象.大对象和小对象的处理方式有很大区别.比如内存碎片整理 —— 在内存中移动大对象的成本是昂贵的,让我们研究一下垃圾回收器是如何处理大对象的,大对象对程序性能有哪 ...

  4. .Net垃圾回收和大对象处理

    本文引自:http://www.cnblogs.com/yukaizhao/archive/2011/11/21/dot_net_gc_large_object_heap.html CLR垃圾回收器根 ...

  5. C#中考虑为大对象使用弱引用

    1.无论怎样尽力,我们总是会使用到某些需要大量内存的数据,而这些内存并不需要经常访问.或许你需要从一个大文件中查找某个特定的值,或者算法需要一个较大的查询表.这时,你也许会采用2中不太好做法:第一种是 ...

  6. [转帖]Oracle数据库lob大对象数据类型字段总结,值得收藏

    Oracle数据库lob大对象数据类型字段总结,值得收藏 原创 波波说运维 2019-07-11 00:02:00 https://www.toutiao.com/i67108943269703357 ...

  7. [翻译] 编写高性能 .NET 代码--第二章 GC -- 避免使用终结器,避免大对象,避免复制缓冲区

    避免使用终结器 如果没有必要,是不需要实现一个终结器(Finalizer).终结器的代码主要是让GC回收非托管资源用.它会在GC完成标记对象为可回收后,放入一个终结器队列里,在由另外一个线程执行队列里 ...

  8. [翻译] 编写高性能 .NET 代码--第二章 GC -- 将长生命周期对象和大对象池化

    将长生命周期对象和大对象池化 请记住最开始说的原则:对象要么立即回收要么一直存在.它们要么在0代被回收,要么在2代里一直存在.有些对象本质是静态的,生命周期从它们被创建开始,到程序停止才会结束.其它对 ...

  9. [翻译] 编写高性能 .NET 代码--第二章 GC -- 减少大对象堆的碎片,在某些情况下强制执行完整GC,按需压缩大对象堆,在GC前收到消息通知,使用弱引用缓存对象

    减少大对象堆的碎片 如果不能完全避免大对象堆的分配,则要尽量避免碎片化. 对于LOH不小心就会有无限增长,但LOH使用的空闲列表机制可以减轻增长的影响.利用这个空闲列表,我们可以在两块分配区域中间找到 ...

  10. 记一起Java大对象引起的FullGC事件及GC知识梳理

    背景 最近发生了一起 Java 大对象引起的 FullGC 事件.记录一下. 有一位商家刷单,每单内有 50+ 商品.然后进行订单导出.订单导出每次会从订单详情服务取100条订单数据.由于 100 条 ...

随机推荐

  1. Spark - [01] 概述

    一.Spark是什么 Spark 是一种基于内存的快速.通用.可扩展的大数据分析引擎. Apache Spark is a unified analytics engine for large-sca ...

  2. [rustGUI][iced]基于rust的GUI库iced(0.13)的部件学习(06):基于iced实现一个简单的图片浏览器

    前言 本文是关于iced库的部件介绍,iced库是基于rust的GUI库,作者自述是受Elm启发. iced目前的版本是0.13.1,相较于此前的0.12版本,有较大改动. 本合集是基于新版本的关于分 ...

  3. 【插件介绍】Mesh2Geom插件

    Mesh to Geometry Plugin,来自达索官方论坛社区 原帖链接:Mesh to Geometry Plugin plugin feature: 允许Abaqus 用户从网格文件生成几何 ...

  4. ChatBI≠NL2SQL:关于问数,聊聊我踩过的坑和一点感悟

    "如果说数据是新时代的石油,智能问数就是能让普通人也能操作的智能钻井平台." 这里是**AI粉嫩特攻队!** ,这段时间真的太忙了,不过放心,关于从零打造AI工具的coze实操下篇 ...

  5. Selenium IDE工具:火狐浏览器实例讲解IDE命令

    在本文中,通过Firefox浏览器上的示例学习Selenium IDE: 我们将使用的网址是"https://accounts.google.com"作为测试程序,通过本文你会 了 ...

  6. Web前端入门第 7 问:HTML 标签不闭合、乱闭合、只有闭合标签有没有什么问题?

    HTML 标签语法遵循层级嵌套的树形结构,如果写出来的代码不是树形结构,浏览器会怎么渲染? 注意:以下截图都来源于 Chrome 浏览器,不同浏览器可能会产生不同的渲染结果. 先看正常代码 <s ...

  7. mac上zsh环境变量如何配置

    环境变量配置 在 macOS 上,如果你使用的是 zsh 作为默认的 shell,那么 /bin/zsh 的环境变量通常可以在以下文件中配置: ~/.zshrc ~/.zprofile ~/.zshe ...

  8. docker login harbor http login登录

    前言 搭建的 harbor 仓库为 http 协议,在本地登录时出现如下报错: docker login http://192.168.xx.xx Username: admin Password: ...

  9. go 定时任务库 cron

    简介 在Linux中,Cron是计划任务管理系统,通过crontab命令使任务在约定的时间执行已经计划好的工作,例如定时备份系统数据.周期性清理缓存.定时重启服务等. 本文介绍的cron库是一个用于管 ...

  10. AITCA联盟:渠道商的革命号角,产业变革的领航者!

    AITCA联盟:渠道商的革命号角,产业变革的领航者! 在AI技术风起云涌的今天,一场无声的革命正在悄然酝酿.在这场革命中,渠道商们不再是被动接受的附庸,而是即将成为改写产业规则.掌握自己命运的主宰者! ...