今天,自己开发的事件驱动的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. Mongodb 相关链接

    http://www.cnblogs.com/lanceyan/tag/mongodb/

  2. LRU 实现缓存

    LRU:Least Recently used 最近最少使用 1.使用LinkedHashMap实现 inheritance实现方式 继承map类 可以使用Collections.synchroniz ...

  3. jieba中文分词.net版

    先看效果: .Net 可以通过NuGet安装 PM> Install-Package jieba.NET 注意 安装之后把Resources文件夹复制到程序集所在目录即可(bun/Debug里面 ...

  4. 初学python之路-day08

    #学习了编码后,还要了解三种字符串.# 一.# 普通字符串:u''以字符作为输出单位,# print(u'abc') # 用于显示abc# # 二进制字符串:b'' 二进制字符串以字节作为输出单位# ...

  5. 控制可编辑的Div 在添加图片,或者@某人的时候 光标移动到最后

    this.$refs.editor.innerHTML += '<span style="color:yellowgreen;">@ 野猪佩奇</span> ...

  6. django-celery配置

    1.项目启动顺序: 启动项目: python manage.py runserver 启动celery beat python manage.py celery beat 启动celery worke ...

  7. 浅谈Java线程安全

    浅谈Java线程安全 - - 2019-04-25    17:37:28 线程安全 Java中的线程安全 按照线程安全的安全程序由强至弱来排序,我们可以将Java语言中各种操作共享的数据分为以下五类 ...

  8. python基础知识练习题(二)

    1. 有两个列表 l1 = [11, 22, 33] l2 = [22, 33, 44] a.获取内容相同的元素列表 li = []l1 = [11, 22, 33] l2 = [22, 33, 44 ...

  9. Linux 添加新用户账号并赋予root权限

    除了root用户之外,通常需要为每个管理创建各自的用户账号,方便每个管理员登录使用, 步骤如下: 1.  添加新用户账号 useradd  mary.lee 2.  为新用户账号设置密码 passwd ...

  10. SSL/TLS抓包出现提示Ignored Unknown Record

    SSL/TLS抓包出现提示Ignored Unknown Record 出现这种提示有两种情况.第一种,抓包迟了,部分SSL/TLS的协商数据没有获取,Wireshark无法识别和解析.第二种,数据包 ...