Java 使用ConcurrentHashMap和AtomicLong似乎会导致内存泄漏,但不确定原因
问题:我的代码有内存泄漏,我没有看到它 期望的结果:我希望有人检查我的代码,并就内存泄漏的位置提供建议 额外积分:如果您能想出一种更好的方法来获取用于死锁检测的线程转储(考虑到下面所述的限制),我将非常有兴趣了解它:) 背景信息:我有一个JVM实例,我一直在尝试诊断一些线程死锁。由于缺少对实例的访问权限,我无法使用JConsole或VisualVM执行此操作,因此我只能在死锁发生时尝试将线程转储写入日志 我选择使用Java 使用ConcurrentHashMap和AtomicLong似乎会导致内存泄漏,但不确定原因,java,multithreading,concurrency,memory-leaks,Java,Multithreading,Concurrency,Memory Leaks,问题:我的代码有内存泄漏,我没有看到它 期望的结果:我希望有人检查我的代码,并就内存泄漏的位置提供建议 额外积分:如果您能想出一种更好的方法来获取用于死锁检测的线程转储(考虑到下面所述的限制),我将非常有兴趣了解它:) 背景信息:我有一个JVM实例,我一直在尝试诊断一些线程死锁。由于缺少对实例的访问权限,我无法使用JConsole或VisualVM执行此操作,因此我只能在死锁发生时尝试将线程转储写入日志 我选择使用ConcurrentHashMap,其中String是线程名称,Long是最后知道
ConcurrentHashMap
,其中String
是线程名称,Long
是最后知道的活动的时间戳。该应用程序是一个Spring Boot应用程序,映射位于启动时创建的配置Bean
中
当我在本地运行代码时,我看到我感兴趣的13个线程的名称被添加到映射中(实际上我只对13个线程中的1个感兴趣,但它们都是由应用程序具有的依赖项创建的,并且在功能上是相关的)。当我在本地运行时,我从未看到映射的大小增长超过13个条目,而且我没有理由认为在目标JVM实例中会出现这种情况。不过,在将代码移动到JVM实例中之后,人们注意到内存一直在缓慢增长。我已经删除了代码,但是我看不到内存泄漏的地方
下面的updateObjectLivelity
方法设置为所有13个线程每秒调用一次。因此,只要其中至少有一个是活动的,该方法就会运行。然后,每5分钟检查一次映射,查看在过去5分钟内是否有任何东西没有更新其时间戳,如果是,则运行线程转储代码
下面是课堂:
class ObjectLivelinessMonitor {
private static final long FIVE_MINS_IN_MILLIS = 300000;
private final ConcurrentHashMap<String, Long> nameOfThreadAndTimeStampOfLastActivity;
private final Consumer<Map.Entry<String, Long>> handlerWhenUnlivelinessDetected;
private final AtomicLong timeStamp;
ObjectLivelinessMonitor(final Consumer<Map.Entry<String, Long>> handlerWhenUnlivelinessDetected) {
this.nameOfThreadAndTimeStampOfLastActivity = new ConcurrentHashMap<>();
this.handlerWhenUnlivelinessDetected = handlerWhenUnlivelinessDetected;
this.timeStamp = new AtomicLong(System.currentTimeMillis());
}
void updateObjectLiveliness(final String nameOfThread) {
final long currentTime = System.currentTimeMillis();
nameOfThreadAndTimeStampOfLastActivity.put(nameOfThread, currentTime);
if((timeStamp.get() + FIVE_MINS_IN_MILLIS) < currentTime) {
timeStamp.set(currentTime);
for(final Map.Entry<String, Long> entry : nameOfThreadAndTimeStampOfLastActivity.entrySet()) {
final long howLongAgoWasLastKnownActivity = currentTime - entry.getValue();
if(howLongAgoWasLastKnownActivity > FIVE_MINS_IN_MILLIS) {
this.handlerWhenUnlivelinessDetected.accept(entry);
}
}
}
}
}
类ObjectLiveLinesMonitor{
私人静态最终长5分钟(单位:毫)=300000;
private final ConcurrentHashMap剩余活动的线程和时间戳名称;
未检测到LiveLines时的专用最终用户句柄;
私有最终原子长时间戳;
ObjectLiveLinesMonitor(未检测到LiveLines时的最终使用者句柄){
this.nameofthereadandtimestampoflastactivity=new ConcurrentHashMap();
this.handlerWhenLiveLinesDetected=handlerWhenLiveLinesDetected;
this.timeStamp=new AtomicLong(System.currentTimeMillis());
}
void updateObjectLiveity(读取的最终字符串名){
最终长currentTime=System.currentTimeMillis();
name of theread和timestampoflastactivity.put(name of theread,currentTime);
if((timeStamp.get()+五分钟(单位:毫秒)5分钟(单位:毫秒)){
此.handler在未检测到LiveLines时接受(条目);
}
}
}
}
}
下面是线程转储代码:
final Consumer<Map.Entry<String, Long>> handlerWhenUnlivelinessDetected =
(final Map.Entry<String, Long> entry) -> {
final String threadDump = Arrays.stream(ManagementFactory.getThreadMXBean()
.dumpAllThreads(true, true))
.map(ThreadInfo::toString)
.collect(Collectors.joining("\n"));
logger.error(entry.getKey() + " has been inactive for " + entry.getValue() + " millis: " + threadDump);
};
final Consumer handler当未检测到LiveLines时=
(最终地图。条目)->{
最后一个字符串threadDump=Arrays.stream(ManagementFactory.getThreadMXBean()
.dumpAllThreads(true,true))
.map(ThreadInfo::toString)
.collect(收集器.加入(“\n”));
logger.error(entry.getKey()+”对于“+entry.getValue()+”毫秒:”+threadDump)一直处于非活动状态;
};
我假设我的错误在我使用
ConcurrentHashMap
或AtomicLong
时的某个地方,但在在线阅读文档并查找这两个类的源代码之后,我看不出我做错了什么。根据线程的活动性进行活动性检查是很奇怪的。当没有线程调用updateObjectLiveity时,您永远不会注意到。此外,您没有告诉线程名称来自何处。对于标准线程池执行器,线程可能会在不活动时终止,并在需要时创建新线程。因此,最多仍有13个线程,但您可能会遇到诸如“thread-14”
、“thread-15”
等名称。但既然您可以访问MX bean,那么调用findDeadlockedThreads()
和/或findMonitorDeadlockedThreads()
?@Holger只要13个线程中的一个处于活动状态,该方法就会运行。如果所有人都死了,就会发出警报。线程名称由依赖项设置,在本地运行时,我看到一致的命名,与“-15”完全不同。线程是实现Runnable
的类,它们的实例被添加到java.util.concurrent.ThreadFactory
。我会尝试使用你推荐的方法。鉴于没有其他人对我的问题做出回应,我认为我对CCM和AtomicLong的使用没有明显的问题,因此这个问题可能是我不知道的其他问题。谢谢,有一个取决于线程的活跃度的活跃度检查是很奇怪的。当没有线程调用updateObjectLiveity时,您永远不会注意到。此外,您没有告诉线程名称来自何处。对于标准线程池执行器,线程可能会在不活动时终止,并在需要时创建新线程。因此,最多仍有13个线程,但您可能会遇到诸如“thread-14”
、“thread-15”
等名称。但是既然您可以访问MX bean,那么调用findDeadl怎么样