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

异常:

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

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大小,上千万个对象没处理,看看该类代码:

package com.mobisummer.spider.master.eventbus;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger; import com.mobisummer.spider.master.ctrlcentre.SpiderController;
import com.mobisummer.spider.master.ctrlcentre.monitor.Monitor;
import com.mobisummer.spider.master.ctrlcentre.monitor.Monitored;
import com.mobisummer.spider.master.eventbus.loop.BloEventGroup;
import com.mobisummer.spider.master.eventbus.loop.NloEventGroup;
import com.mobisummer.spider.master.eventbus.model.EventType;
import com.mobisummer.spider.master.eventbus.model.EventTask; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; /**
* 消息总线-双向队列
* @date 2019年1月9日
*/
@Component
public class EventBus implements Monitored { private static Logger log = LoggerFactory.getLogger("event");
private LinkedBlockingQueue<EventTask> taskRegistered = new LinkedBlockingQueue<>(); // wait to be handled
private ConcurrentLinkedQueue<EventTask> taskPool = new ConcurrentLinkedQueue<>(); // for reuse
private ConcurrentHashMap<Integer,AtomicInteger> eventCount =new ConcurrentHashMap<>(); @Autowired
SpiderController spiderController; public EventBus() {
Monitor.register(this);
} /**
* add event to queue tail
*
* @param eventType
* @param itemId
* @param object
*/
public void push(String itemId,int eventType, Object object) {
EventTask spiderEvent = retrieveEvent(eventType, itemId, object,spiderController);
try {
if(object==null){
System.out.println("");
}
if(spiderEvent.object==null){
System.out.println("");
}
taskRegistered.put(spiderEvent);
} catch (InterruptedException e) {
log.error("put item to queue cause an exception", e);
}
log.debug("add an event to tail of the queue, item id is {} , type is {}. the queue size is {} now", itemId, EventType
.getDescription(spiderEvent.eventType), taskRegistered.size());
} private EventTask retrieveEvent(int eventType, String itemId, Object object,EventHandler handler) {
EventTask spiderEvent = taskPool.poll();
if(spiderEvent == null) {
spiderEvent = new EventTask(eventType, itemId, object,handler);
} else {
spiderEvent.eventType = eventType;
spiderEvent.itemId = itemId;
spiderEvent.object = object;
spiderEvent.handler =handler;
}
return spiderEvent;
} /**
* 循环消费需要处理的事件
*/
public void loop() {
while (true) { }
} public void releaseEvent(EventTask spiderEvent) {
spiderEvent.object = null;
taskPool.add(spiderEvent);
} public static void eventError(Integer eventType, String itemId, Object object,Exception e){
String errDesc= "handle event cause an exception ======" +e.toString()+"======";
if(eventType!=null){
errDesc = errDesc+eventType ;
}
if(itemId!=null){
errDesc =errDesc+itemId;
}
if(object!=null){
errDesc =errDesc+object.toString();
}
log.error(errDesc);
} @Override
public Object getHealthIndex() {
String desc ="";
for(Integer event :eventCount.keySet()){
desc =desc+"\n事件:"+EventType.getDescription(event)+" 该事件共产生次数"+eventCount.get(event);
}
return desc;
} private void countEvent(int eventType){
AtomicInteger count = eventCount.get(eventType);
if(count==null){
count = new AtomicInteger();
eventCount.put(eventType,count);
}else {
count.incrementAndGet();
}
}
}

  这是一个事件总线,可以看出因为堆溢出,不断的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. Ansible运维自动化工具19个常用模块使用实例【转】

    一.模块列表 1.setup 2.ping 3.file 4.copy 5.command 6.shell 7.script 8.cron 9.yum 10.service 11.group 12.u ...

  2. hammer.js初探

    hammer.js官方文档 hammerjs是什么 hammerjs是一个短小精悍的库,他可以让我们轻松的实现移动端上的手势. hammerjs的两大优势如下: 为移动端网页添加相关手势. 去除移动端 ...

  3. WPF 10天修炼 第十天- WPF数据绑定

    WPF数据绑定 数据绑定到元素属性是将源对象指定为一个WPF元素,并且源属性是一个依赖属性,依赖属性内置了变更通知.当改变源对象依赖属性值之后,绑定目标可以立即得到更新,开发人员不需要手动编写响应事件 ...

  4. Xshell 连接虚拟机出现 "The remote SSH server rejected X11 forwarding request"

    1. 描述 虚拟机:VirtualBox Linux: centOS7 解决了 centOS7在VirtualBox中装好后的网络连接问题 后,用 Xshell 连接服务器时出现下面情况: 2. ss ...

  5. cdh集群ip更改

    #---1.修改每个用户的hosts vi /etc/hosts #127.0.0.1 localhost localhost.localdomain localhost4 localhost4.lo ...

  6. LeetCode 14.Longest Common Prefix(C++)

    最长公共前缀问题,考虑没有或只有一个字符串的情况,然后只需要逐个比对就可以了. class Solution { public: string longestCommonPrefix(vector&l ...

  7. OpenStack--ntp组件时间同步服务

    作用:ntp主要是用于对计算机的时间同步管理操作 环境: 服务端: 192.168.245.172 客户端: 192.168.245.171 时间是对服务器来说是很重要的,一般很多网站都需要读取服务器 ...

  8. python Request模块

    ---恢复内容开始--- Request的五种请求方式: request.get() request.post() request.head() requst.put() request.patch( ...

  9. [原创]全新IFPGA-Cable----支持Xilinx/Altera/Lattice JTAG和UART

    Xilinx   平台:ISE 14.7/Vivado 2014.4+: Lattice 平台:Diamond软件自动识别,免驱动: Altera  平台:安装相关插件,支持: 串           ...

  10. python 10大算法之二 LogisticRegression 笔记

    使用的包 import matplotlib.pyplot as plt import pandas as pd import numpy as npfrom sklearn import datas ...