今天,自己开发的事件驱动的java大规模爬虫程序上线了几个新任务后突然异常。

异常:

   程序业务异常,经查看CPU利用率满,内存满,一直报OOM,目测有内存泄露。如下图所示,四核16G的内粗,CPU高达400%,内存使用了 6G,刚好爆满,JVM启动参数为:

  1. java -server -Xmx6114M -Xms2048M -XX:+PrintGCDetails -XX:+PrintGCApplicationStoppedTime -Xloggc:/home/ubuntu/logs/spider/master/gc.log -XX:+PrintGCDateStamps -XX:SurvivorRatio= -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/ubuntu/heap/spider/master/dump -jar /home/ubuntu/apps/spider-service/spider-master/current

内存截图:

分析:

  首先找java进程为13419,找到该进程的什么线程使用最多,运行top -p 13419 -H  得到如下截图,一直观察得出,有四个线程一直占用100%的CPU;

  于是设法找到CPU的线程的调用栈,运行 jstack 13419 >表示 jstack.log 查看文件信息,发现没有对应线程的状态,也就找不到调用栈(注意,想找线程nid,可能需要转换16进制表示,因为线程nid在jstack.log中为16进制)

  

  想到这里,已经知道,该上面四个线程应该是GC线程,所以找不到,因此确定到堆文件分析;也就是初步断定没有死锁,而是堆溢出,但不一定是内存泄露;到这里,当然要去看看堆的GC情况了,运行jstat看看,如下图(S0为survive0区,S1为survive1区,而U代表使用,C代表总大小,E、O、M分别代表年轻代,年老代,Meta区,T为时间),不断的YGC而没有释放内存。

  在重量级的分析堆文件之前,我们可以运行jmap -histo 13419 >out.log查看对象的数量和持有内存分布,得到个大体情况;然后运行 jmap -dump:live,format=b,file=heap-dump.bin 13419  获得堆文件;其实jhat是可以分析堆文件的,但是我最终用了eclipse插件;

代码定位:

  最后定位到一个EventBus类持有5G大小,上千万个对象没处理,看看该类代码:

  1. package com.mobisummer.spider.master.eventbus;
  2.  
  3. import java.util.concurrent.ConcurrentHashMap;
  4. import java.util.concurrent.ConcurrentLinkedQueue;
  5. import java.util.concurrent.LinkedBlockingQueue;
  6. import java.util.concurrent.atomic.AtomicInteger;
  7.  
  8. import com.mobisummer.spider.master.ctrlcentre.SpiderController;
  9. import com.mobisummer.spider.master.ctrlcentre.monitor.Monitor;
  10. import com.mobisummer.spider.master.ctrlcentre.monitor.Monitored;
  11. import com.mobisummer.spider.master.eventbus.loop.BloEventGroup;
  12. import com.mobisummer.spider.master.eventbus.loop.NloEventGroup;
  13. import com.mobisummer.spider.master.eventbus.model.EventType;
  14. import com.mobisummer.spider.master.eventbus.model.EventTask;
  15.  
  16. import org.slf4j.Logger;
  17. import org.slf4j.LoggerFactory;
  18. import org.springframework.beans.factory.annotation.Autowired;
  19. import org.springframework.stereotype.Component;
  20.  
  21. /**
  22. * 消息总线-双向队列
  23. * @date 2019年1月9日
  24. */
  25. @Component
  26. public class EventBus implements Monitored {
  27.  
  28. private static Logger log = LoggerFactory.getLogger("event");
  29. private LinkedBlockingQueue<EventTask> taskRegistered = new LinkedBlockingQueue<>(); // wait to be handled
  30. private ConcurrentLinkedQueue<EventTask> taskPool = new ConcurrentLinkedQueue<>(); // for reuse
  31. private ConcurrentHashMap<Integer,AtomicInteger> eventCount =new ConcurrentHashMap<>();
  32.  
  33. @Autowired
  34. SpiderController spiderController;
  35.  
  36. public EventBus() {
  37. Monitor.register(this);
  38. }
  39.  
  40. /**
  41. * add event to queue tail
  42. *
  43. * @param eventType
  44. * @param itemId
  45. * @param object
  46. */
  47. public void push(String itemId,int eventType, Object object) {
  48. EventTask spiderEvent = retrieveEvent(eventType, itemId, object,spiderController);
  49. try {
  50. if(object==null){
  51. System.out.println("");
  52. }
  53. if(spiderEvent.object==null){
  54. System.out.println("");
  55. }
  56. taskRegistered.put(spiderEvent);
  57. } catch (InterruptedException e) {
  58. log.error("put item to queue cause an exception", e);
  59. }
  60. log.debug("add an event to tail of the queue, item id is {} , type is {}. the queue size is {} now", itemId, EventType
  61. .getDescription(spiderEvent.eventType), taskRegistered.size());
  62. }
  63.  
  64. private EventTask retrieveEvent(int eventType, String itemId, Object object,EventHandler handler) {
  65. EventTask spiderEvent = taskPool.poll();
  66. if(spiderEvent == null) {
  67. spiderEvent = new EventTask(eventType, itemId, object,handler);
  68. } else {
  69. spiderEvent.eventType = eventType;
  70. spiderEvent.itemId = itemId;
  71. spiderEvent.object = object;
  72. spiderEvent.handler =handler;
  73. }
  74. return spiderEvent;
  75. }
  76.  
  77. /**
  78. * 循环消费需要处理的事件
  79. */
  80. public void loop() {
  81. while (true) {
  82.  
  83. }
  84. }
  85.  
  86. public void releaseEvent(EventTask spiderEvent) {
  87. spiderEvent.object = null;
  88. taskPool.add(spiderEvent);
  89. }
  90.  
  91. public static void eventError(Integer eventType, String itemId, Object object,Exception e){
  92. String errDesc= "handle event cause an exception ======" +e.toString()+"======";
  93. if(eventType!=null){
  94. errDesc = errDesc+eventType ;
  95. }
  96. if(itemId!=null){
  97. errDesc =errDesc+itemId;
  98. }
  99. if(object!=null){
  100. errDesc =errDesc+object.toString();
  101. }
  102. log.error(errDesc);
  103. }
  104.  
  105. @Override
  106. public Object getHealthIndex() {
  107. String desc ="";
  108. for(Integer event :eventCount.keySet()){
  109. desc =desc+"\n事件:"+EventType.getDescription(event)+" 该事件共产生次数"+eventCount.get(event);
  110. }
  111. return desc;
  112. }
  113.  
  114. private void countEvent(int eventType){
  115. AtomicInteger count = eventCount.get(eventType);
  116. if(count==null){
  117. count = new AtomicInteger();
  118. eventCount.put(eventType,count);
  119. }else {
  120. count.incrementAndGet();
  121. }
  122. }
  123. }

  这是一个事件总线,可以看出因为堆溢出,不断的GC,处理事件的是一个单线程根本没有被调度的机会,或许已经挂了!堆积事件不处理,这个不是内存泄露,只是系统崩盘了,最后重构调度代码,重视调度,构建调度抽象:

重构:

  先把事件分类,绑定事件和控制器,做成状态机,然后通过EventBus分配给阻塞的Blo执行或者非阻塞的Nlo执行,而且各自分配Eventloop去执行;重构后上线,加大压测;能承受量加大了!不过系统还是要有机制拒绝服务才行的,这个就是后话了。   

记一次JVM故障排除的更多相关文章

  1. JVMTI 中间JNI系列功能,线程安全和故障排除技巧

    JVMTI 中间JNI系列功能,线程安全和故障排除技巧 jni functions 在使用 JVMTI 的过程中,有一大系列的函数是在 JVMTI 的文档中 没有提及的,但在实际使用却是很实用的. 这 ...

  2. ORA-4031 错误故障排除与诊断[视频] (Doc ID 2016002.1)

    Copyright (c) 2019, Oracle. All rights reserved. Oracle Confidential.     ORA-4031 错误故障排除与诊断[视频] (Do ...

  3. spark——故障排除

    故障排除一:控制reduce端缓冲大小以避免OOM 在Shuffle过程,reduce端task并不是等到map端task将其数据全部写入磁盘后再去拉取,而是map端写一点数据,reduce端task ...

  4. 学习笔记:CentOS7学习之十八:Linux系统启动原理及故障排除

    目录 学习笔记:CentOS7学习之十八:Linux系统启动原理及故障排除 18.1 centos6系统启动过程及相关配置文件 18.1.1 centos6系统启动过程 18.1.2 centos6启 ...

  5. linux下抓取tomcat相关内存、线程、文件句柄等快照,用于故障排除。

    以下脚本推荐放在定时任务里,写好cron表达式,在不影响业务系统的情况下dump一些信息分析系统性能瓶颈以及故障排除. 因为每次dump的时候jvm会暂停(几秒到几十秒不等).所以在生产系统使用时慎用 ...

  6. spark 性能优化 数据倾斜 故障排除

    版本:V2.0 第一章       Spark 性能调优 1.1      常规性能调优 1.1.1   常规性能调优一:最优资源配置 Spark性能调优的第一步,就是为任务分配更多的资源,在一定范围 ...

  7. 使用JDK工具进行Java服务器应用程序故障排除

    Java性能调优指南–有关提高Java代码性能的各种技巧. 最近又学到了很多新知识,感谢优锐课老师细致地讲解,这篇博客记录下自己所学所想. 1. 介绍 在Java世界中,我们大多数人习惯于在Java应 ...

  8. 坑爹坑娘坑祖宗的87端口(记一次tomcat故障排查)

    原贴如下 坑爹坑娘坑祖宗的87端口(记一次tomcat故障排查) 虽然我用的是PHPstudy部署的dedecms,还是一样栽倒这个坑里了. 总结经验:本地测试使用8000~9000的端口比较安全.

  9. 理解 OpenStack + Ceph (7): Ceph 的基本操作和常见故障排除方法

    本系列文章会深入研究 Ceph 以及 Ceph 和 OpenStack 的集成: (1)安装和部署 (2)Ceph RBD 接口和工具 (3)Ceph 物理和逻辑结构 (4)Ceph 的基础数据结构 ...

随机推荐

  1. FATAL ERROR: please install the following Perl modules before executing

    运行安装mysql 报错 [root@localhost mysql-mult]# ./scripts/mysql_install_db  --defaults-file=conf/3306my.cn ...

  2. [Linux]fcntl函数文件锁概述

    概述 fcntl函数文件锁有几个比较容易忽视的地方: 1.文件锁是真的进程之间而言的,调用进程绝对不会被自己创建的锁锁住,因为F_SETLK和F_SETLKW命令总是替换调用进程现有的锁(若已存在), ...

  3. oracle wallet使用与维护

    oracle wallet使用与维护2015年05月26日 17:58:55 SilenceRiver 阅读数:1614oracle wallet使用与维护---oracle无密码登录分类: Orac ...

  4. 先进过程控制之一:浅说APC

    先进过程控制(APC)技术作为在生产装置级的信息化应用,在优化装置的控制水平和提高生产过程的管理水平的同时,还为企业创造了可观的经济效益. 1.什么是APC 先进过程控制,简称APC,并不是什么新概念 ...

  5. JAVA第三次实训作业

    ---恢复内容开始--- 1. 编写“学生”类及其测试类. “学生”类: 类名:Student 属性:姓名.性别.年龄.学号.5门课程的成绩 方法1:在控制台输出各个属性的值. 方法2:计算平均成绩 ...

  6. C语言博客作业2--循环结构

    1.1 思维导图 1.2 本章学习体会及代码量学习体会 1.2.1 学习体会 对本章学习感觉相对前面的难度有较大提升,而且刚开始对嵌套循环比较陌生,像龟兔赛跑和输出菱形都是用了较长时间才完成,所以我认 ...

  7. mysql 函数学习(常用)

    1.时间函数(组1) SELECT CURRENT_DATE() AS date, CURRENT_TIME() AS `time`, CURRENT_TIMESTAMP() AS `timestam ...

  8. mysql把一个查询结果当作一个子集来查询

    SELECT * FROM (SELECT * FROM table  GROUP BY column HAVING COUNT(column)>=3 ORDER BY column DESC ...

  9. Outlook IMAP 修改PST文件存储路径

    IMAP类型的账户修改PST文件位置方法: 对于IMAP类型账户的PST文件,既没有“修改文件夹”的选项,也无法按OFFICE官方操作指南中的操作.因为每次Outlook只要检测到默认路径下的PST文 ...

  10. 【Linux】在Win10上搭建WSL(适用于Linux的Windows子系统)

    1.打开WSL  ,控制面板 -- 程序 -- 程序和功能 -- 打开或关闭Windows功能 - 选中[适用于Linux的Windows子系统] 2.开启后重启电脑 3.在Win10自带的Micro ...