记一次JVM故障排除
今天,自己开发的事件驱动的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故障排除的更多相关文章
- JVMTI 中间JNI系列功能,线程安全和故障排除技巧
JVMTI 中间JNI系列功能,线程安全和故障排除技巧 jni functions 在使用 JVMTI 的过程中,有一大系列的函数是在 JVMTI 的文档中 没有提及的,但在实际使用却是很实用的. 这 ...
- ORA-4031 错误故障排除与诊断[视频] (Doc ID 2016002.1)
Copyright (c) 2019, Oracle. All rights reserved. Oracle Confidential. ORA-4031 错误故障排除与诊断[视频] (Do ...
- spark——故障排除
故障排除一:控制reduce端缓冲大小以避免OOM 在Shuffle过程,reduce端task并不是等到map端task将其数据全部写入磁盘后再去拉取,而是map端写一点数据,reduce端task ...
- 学习笔记:CentOS7学习之十八:Linux系统启动原理及故障排除
目录 学习笔记:CentOS7学习之十八:Linux系统启动原理及故障排除 18.1 centos6系统启动过程及相关配置文件 18.1.1 centos6系统启动过程 18.1.2 centos6启 ...
- linux下抓取tomcat相关内存、线程、文件句柄等快照,用于故障排除。
以下脚本推荐放在定时任务里,写好cron表达式,在不影响业务系统的情况下dump一些信息分析系统性能瓶颈以及故障排除. 因为每次dump的时候jvm会暂停(几秒到几十秒不等).所以在生产系统使用时慎用 ...
- spark 性能优化 数据倾斜 故障排除
版本:V2.0 第一章 Spark 性能调优 1.1 常规性能调优 1.1.1 常规性能调优一:最优资源配置 Spark性能调优的第一步,就是为任务分配更多的资源,在一定范围 ...
- 使用JDK工具进行Java服务器应用程序故障排除
Java性能调优指南–有关提高Java代码性能的各种技巧. 最近又学到了很多新知识,感谢优锐课老师细致地讲解,这篇博客记录下自己所学所想. 1. 介绍 在Java世界中,我们大多数人习惯于在Java应 ...
- 坑爹坑娘坑祖宗的87端口(记一次tomcat故障排查)
原贴如下 坑爹坑娘坑祖宗的87端口(记一次tomcat故障排查) 虽然我用的是PHPstudy部署的dedecms,还是一样栽倒这个坑里了. 总结经验:本地测试使用8000~9000的端口比较安全.
- 理解 OpenStack + Ceph (7): Ceph 的基本操作和常见故障排除方法
本系列文章会深入研究 Ceph 以及 Ceph 和 OpenStack 的集成: (1)安装和部署 (2)Ceph RBD 接口和工具 (3)Ceph 物理和逻辑结构 (4)Ceph 的基础数据结构 ...
随机推荐
- 【转】Python3—UnicodeEncodeError 'ascii' codec can't encode characters in position 0-1
转自:https://blog.csdn.net/AckClinkz/article/details/78538462 环境 >>> import sys >>> ...
- Qt for Android开发总结
近段时间,本人使用Qt5.3.0开发了Android应用,由于官方资料较少,在此记录开发过程遇到的问题及解决方法 1.Android平台的视频播放,只能使用qml的MediaPlayer 2.qml中 ...
- 11:57:24 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] WARN o.apache.kafka.clients.NetworkClient - [Consumer clientId=consumer-2, groupId=jiatian_api] 3 partitions have leader……
错误如下: 11:57:24 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] WARN o.apache.kaf ...
- Centos6.8 yum安装MySQL5.6
第一步:安装仓库wget http://repo.mysql.com//mysql57-community-release-el6-8.noarch.rpmrpm -ivh mysql-communi ...
- jenkins主要目录用途
主目录 除了Jenkins的WAR包所在目录,Jenkins还有一个更重要的目录——Jenkins的所有重要数据都存放在这个独立的目录中,称为Jenkins主目录,它的默认位置是在当前用户根目录的隐藏 ...
- 解决tcp粘包问题
目录 什么是粘包(演示粘包现象) 解决粘包 实际应用 什么是粘包 首先只有tcp有粘包现象,udp没有粘包 socket收发消息的原理 发送端可以是一K一K地发送数据,而接收端的应用程序可以两K两K地 ...
- JMM以及并发三大特性介绍(包括解决方案)
JMM结构图: JMM对同步的8种操作: JMM的同步规则: Countdownlatch介绍: 该类功能是可以阻塞线程,并在保证线程满足特定条件下,继续执行.如上图,Countdownlatch的c ...
- apache http添加证书转成https
使用yum安装的apache. 第一步,需要把准备好的证书上传到服务器.具体位置:/app/cacerts/ 第二步,安装ssl. 命令:yum install mod_ssl openssl 安装完 ...
- inotify+rsync文件实时同步报错:usr/local/bin/inotifywait: error while loading shared libraries: libinotifytools.so.0:
解决办法: ln -sv /usr/local/lib/libinotify* /usr/lib/ /usr/lib64/libinotifytools.so.
- SQL对某个字段进行排名
SELECT ( ) AS rowno, a.badge,a.NAME,a.direct_evaluate_rate,a.view_rate FROM ( SELECT * FROM `hrs_sta ...