hazelcast 提供了3中方法调用startCleanup:



第一种是在ConcuurentMapManager的构造函数中,通过调用node的executorManager中的ScheduledExecutorService来创建每秒运行一次cleanup操作的线程(代码例如以下)。

因为这是ConcuurentMapManager构造函数的代码,所以这样的调用startCleanup的操作是默认就会有的。

node.executorManager.getScheduledExecutorService().scheduleAtFixedRate(new Runnable() {
publicvoid run() {
for (CMap cMap : maps.values()) {
cMap.startCleanup(false);
}
}
}, 1, 1, TimeUnit.SECONDS);

另外一种是通过配置文件来触发startCleanup的运行。配置 PutOperationhandlerif overcapacity policy。我们系统的配置文件没有配置这方面的policy,全部这样的方式在我们系统中没有使用。

第三种是自己直接写代码去调用startCleanup函数(public方法。线程安全的). 这个没有实如今我们的系统中。

所以我的调查方向放在了第一种调用的情况,hazelcast里面的ScheduledExecutorService是通过java.util.ScheduledThreadPoolExecutor 来实现的.

esScheduled = new ScheduledThreadPoolExecutor(5, new ExecutorThreadFactory(node.threadGroup,
node.getThreadPoolNamePrefix("scheduled"), classLoader), new RejectionHandler()) {
protected void beforeExecute(Thread t, Runnable r) {
threadPoolBeforeExecute(t, r);
}
}

查看ScheduledThreadPoolExecutor的实现,它把线程实现分成了3个部分: runnable tasks可运行任务, workers to execute the tasks运行任务的详细线程 以及 ScheduledThreadPoolExecutor 调度workers依照要求运行runnable tasks。

我们通过scheduleAtFixdRate提交了task,scheduleAtFixedRate先把它打包成反复运行的ScheduleFutureTask

<pre name="code" class="java">    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
if (period <= 0)
throw new IllegalArgumentException();
RunnableScheduledFuture<? > t = decorateTask(command,
new <strong>ScheduledFutureTas</strong>k<Object>(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(period)));
delayedExecute(t);
return t;
}

ScheduleFutureTask的run方法实现又一次schedule:

public void  run() {
boolean periodic = isPeriodic();
if (!canRunInCurrentRunState(periodic))
cancel(false);
else if (!periodic)
ScheduledFutureTask.super.run();
else if (ScheduledFutureTask.super.runAndReset()) {
setNextRunTime();
<strong> reExecutePeriodic(outerTask);</strong>
}
}

delayedExecute里面假设当前worker的数目小于初始化定义的CorePool的数目,就创建新的worker线程,然后把task放到queue里面

private void delayedExecute(Runnable command) {
if (isShutdown()) {
reject(command);
return;
}
// Prestart a thread if necessary. We cannot prestart it
// running the task because the task (probably) shouldn't be
// run yet, so thread will just idle until delay elapses.
if (getPoolSize() < getCorePoolSize())
prestartCoreThread(); <strong> super.getQueue().add(command);</strong>
}
public boolean prestartCoreThread() {
return addIfUnderCorePoolSize(null);
}
private boolean addIfUnderCorePoolSize(Runnable firstTask) {
Thread t = null;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (poolSize < corePoolSize && runState == RUNNING)
t = addThread(firstTask);
} finally {
mainLock.unlock();
}
return t != null;
}
private Thread addThread(Runnable firstTask) {
Worker w = new Worker(firstTask);
Thread t = threadFactory.newThread(w);
boolean workerStarted = false;
if (t != null) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
w.thread = t;
workers.add(w);
int nt = ++poolSize;
if (nt > largestPoolSize)
largestPoolSize = nt;
try {
t.start();
workerStarted = true;
}
finally {
if (!workerStarted)
workers.remove(w);
}
}
return t;
}

全部启动的worker就做一件事情,从queue中取task运行

     try {
hasRun = true;
Runnable task = firstTask;
firstTask = null;
while (task != null || (task = <strong>getTask</strong>()) != null) {
<strong>runTask(task);</strong>
task = null;
}
} finally {
workerDone(this);
}
}
}
Runnable getTask() {
<strong> for (;;) {</strong>
try {
int state = runState;
if (state > SHUTDOWN)
return null;
Runnable r;
if (state == SHUTDOWN) // Help drain queue
r = workQueue.poll();
else if (poolSize > corePoolSize || allowCoreThreadTimeOut)
r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);
else
<strong> r = workQueue.take();</strong>
if (r != null)
return r;
if (workerCanExit()) {
if (runState >= SHUTDOWN) // Wake up others
interruptIdleWorkers();
return null;
}
// Else retry
} catch (InterruptedException ie) {
// On interruption, re-check runState
}
}
}
private void runTask(Runnable task) {
final ReentrantLock runLock = this.runLock;
runLock.lock();
try {
if ((runState >= STOP ||
(Thread.interrupted() && runState >= STOP)) &&
hasRun)
thread.interrupt();
boolean ran = false;
beforeExecute(thread, task);
<strong> try {
task.run();
ran = true;
afterExecute(task, null);
++completedTasks;
} catch (RuntimeException ex) {
if (!ran)
afterExecute(task, ex);
throw ex;
}</strong>
} finally {
runLock.unlock();
}
}

了解了java threadpool的工作原理之后。我们能够知道。startCleanup是代码pass给ScheduledThreadPoolExecutor的runnable task,它不被运行,可能的原因有:

1. ScheduledThreadPoolExecutor初始化时候出错,task全然没有提交成功。因为lastCleanup并非系统应用的启动时间,已经过了几个月了,所以。非常明显在系统初始化的时候,esScheduled(ScheduledThreadPoolExecutor)还是正常工作的,仅仅是突然在2月4号停止了工作,所以这样的可能性能够排除。

2.    Worker 没有正常工作。不在从ScheduledThreadPoolExecutor的queue里面取数据,这个非常快就被我排除了:

首先heap dump中有5个pending workers in esScheduled (0/2/3/5/9):

其次从thread dump中能够看出,这五个线程都是在等着从queue里面取数据:

    ……
<strong> at java/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2025)[optimiz</strong>ed]
at java/util/concurrent/DelayQueue.take(DelayQueue.java:164)[optimized]
at java/util/concurrent/ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:609)[inlined]
at java/util/concurrent/ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:602)[optimized]
at java/util/concurrent/ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:947)[optimized]
at java/util/concurrent/ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:907)
at java/lang/Thread.run(Thread.java:662)
at jrockit/vm/RNI.c2java(JJJJJ)V(Native Method)
-- end of trace
hz._hzInstance_1_com.ericsson.ngin.session.ra.hazelcast.scheduled.thread-2" id=51 idx=0xd8 tid=32639 prio=5 alive, parked, native_blocked
hz._hzInstance_1_com.ericsson.ngin.session.ra.hazelcast.scheduled.thread-3" id=52 idx=0xdc tid=32640 prio=5 alive, parked, native_blocked
hz._hzInstance_1_com.ericsson.ngin.session.ra.hazelcast.scheduled.thread-4" id=53 idx=0xe0 tid=32641 prio=5 alive, parked, native_blocked
hz._hzInstance_1_com.ericsson.ngin.session.ra.hazelcast.scheduled.thread-5" id=75590 idx=0x3cc tid=3308 prio=5 alive, parked, native_blocked

所以worker不正常也被排除了。

3.  我们提交给系统的runner task自己主动从queue里面消失了,从memory dump中确实发现queue没有tasks了



而没有task的原因非常明显是由于当前task运行完之后没有又一次reschedule,至于原因,由于scheduledFutrueTask已经不存在,无法从memory dump和thread dump中分析出结果,成为了一个谜。。

。。

public void  run() {
boolean periodic = isPeriodic();
if (!canRunInCurrentRunState(periodic))
cancel(false);
else if (!periodic)
ScheduledFutureTask.super.run();
else if (ScheduledFutureTask.super.runAndReset()) {
setNextRunTime();
<strong> reExecutePeriodic(outerTask);</strong>
}
}

实战Java内存泄漏问题分析 -- hazelcast2.0.3使用时内存泄漏 -- 2的更多相关文章

  1. Java 中无返回值的方法在使用时应该注意的问题

    Java 中的方法是形态多样的.无返回值的方法在使用时应该规避哪些问题呢? 一.不可以打印调用或是赋值调用,只能是单独调用(非常重要): 二.返回值没有,不代表参数就没有: 三.不能return一个具 ...

  2. java中 try catch finally和return联合使用时,代码执行顺序的小细节

    代码1测试 public static void main(String[] args) { aa(); } static int aa() { try { int a=4/0; } catch (E ...

  3. Java内存泄漏及分析

    对于内存泄漏,首先想到的是C语言,其实不然,java中也有各种的内存泄漏.对于java程序员,在虚拟即中,不需要为每一个新建对象去delete/free内存,不容易出现内存泄漏.但是,正 是由于这种机 ...

  4. Web 2.0 浏览器端可靠性测试第2部分(如何发现和分析 Web 2.0 浏览器端的内存泄漏)

    介绍浏览器端的可靠性测试 在上一编文章中我们介绍了浏览器端可靠性测试的概念.测试方法.以及常用的测试和分析工具.我们知道,浏览器端可靠性测试,就是以浏览器为测试平台,通过模拟用户在真实场景下的页面操作 ...

  5. JAVA 从GC日志分析堆内存 第七节

    JAVA 从GC日志分析堆内存 第七节   在上一章中,我们只设置了整个堆的内存大小.但是我们知道,堆又分为了新生代,年老代.他们之间的内存怎么分配呢?新生代又分为Eden和Survivor,他们的比 ...

  6. 五、jdk工具之jmap(java memory map)、 mat之四--结合mat对内存泄露的分析、jhat之二--结合jmap生成的dump结果在浏览器上展示

    目录 一.jdk工具之jps(JVM Process Status Tools)命令使用 二.jdk命令之javah命令(C Header and Stub File Generator) 三.jdk ...

  7. Day 18: 记filebeat内存泄漏问题分析及调优

    ELK 从发布5.0之后加入了beats套件之后,就改名叫做elastic stack了.beats是一组轻量级的软件,给我们提供了简便,快捷的方式来实时收集.丰富更多的数据用以支撑我们的分析.但由于 ...

  8. 通过 thread dump 分析找到高CPU耗用与内存溢出的Java代码

    http://heylinux.com/archives/1085.html通过 thread dump 分析找到高CPU耗用与内存溢出的Java代码 首先,要感谢我的好朋友 钊花 的经验分享. 相信 ...

  9. Java 内存查看与分析

    1:gc日志输出 在jvm启动参数中加入 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimestamps -XX:+PrintGCApplication ...

随机推荐

  1. 【DP、线段树优化】琪露诺

    跟去年(2017)PJ第四题几乎是一样的?/吐血 DP方程可以很简单的推出来,f[i]=max{f[k]}+a[i] 然而这样做是O(n^2)的 看一下数据,200000的话要不nlogn 要不n 由 ...

  2. ThreadLocal类详解

    学习一个东西首先要知道为什么要引入它,就是我们能用它来干什么.所以我们先来看看ThreadLocal对我们到底有什么用,然后再来看看它的实现原理. ThreadLocal如果单纯从名字上来看像是“本地 ...

  3. [转载]Android平台第三方应用分享到微信开发

    一.申请APPID 微信公共平台和微博分享一样,也需要申请一个ID,来作为调起微信.分享到微信的唯一标识. 申请微信APPID可以到微信平台http://open.weixin.qq.com/app/ ...

  4. .net core2.0 中使用DB First

    一.新建一个控制台测试项目 1.1.添加引用 1.2.修改项目文件 1.3.添加红框的内容 <ItemGroup> <DotNetCliToolReference Include=& ...

  5. SQLServer In和Exists

    In          Exists () 1分42秒  5秒 Exists() 返回布尔值 如果子查询结果行>0,则返回 TRUE. 反之返回FALSE exists(select * fro ...

  6. I2C controller core之Bit controller(02)

    4 generate clock and control signals 1 -- architecture signal iscl_oen, isda_oen : std_logic; -- int ...

  7. c#动态类型Dynamic

    需引用System.Dynamic命名空间 来源:http://www.cnblogs.com/ryanding/archive/2010/12/09/1900106.html dynamic Cus ...

  8. TextInputLayout使用时各个地方的字体颜色

    我们现在在做Android端的输入框时,要具备如下功能: 默认提示获取焦点时提示上移至输入框顶部获取焦点时输入框有提示错误时增加错误提示直接上图: 默认情况: 获取焦点时: 开始输入文字时: 有错误时 ...

  9. hadoop fs 常用命令(1)

    Hadoop: https://blog.csdn.net/mulangren1988/article/details/54860924 Hadoop:1. Hadoop fs –fs [local ...

  10. S-HR远程调试