Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

线上死锁 #914

Open
vibodse opened this issue Aug 21, 2024 · 11 comments
Open

线上死锁 #914

vibodse opened this issue Aug 21, 2024 · 11 comments

Comments

@vibodse
Copy link

vibodse commented Aug 21, 2024

版本

2.7.5

场景

这是线上的一个死锁场景,服务使用的xxl-job跑的定时任务,任务中调用了jetcache注解缓存的方法,跑一段时间定时任务就都停了,查看线程发现是死锁了,具体原因不清楚,因为我技术一般,大佬如果有空帮忙可以看一眼。
死锁截图

@areyouok
Copy link
Collaborator

这不是死锁,只是慢,你是不是本地缓存太多了。还有那个缓存失效通知,默认是关闭的,无脑打开只会死得快

@areyouok
Copy link
Collaborator

我看了下这里代码没有问题,性能可以略微优化,我稍后改改,但解决不了你的本质问题

@areyouok
Copy link
Collaborator

本地cache多可以用CaffeineCache

@vibodse
Copy link
Author

vibodse commented Aug 22, 2024

本地cache多可以用CaffeineCache

好的,感谢您的回复,我尝试优化一下。

@HetaoWangl
Copy link

HetaoWangl commented Sep 11, 2024

本地cache多可以用CaffeineCache

我也遇到了一样的问题,应该是死锁了,运行一段时间后,服务的句柄数就会无限增长,因为通过linkedHashMap获取缓存值时,无法获取到锁,所以所有的请求都会阻塞,最终随着时间会出现无数个阻塞的线程,通过jstack查看,是LinkedHashMapCache.cleanExpiredEntry现成一直没有释放写锁资源。我的版本是2.7.6,补充一下,我的缓存数据量不多,大概只有10-20个之间,并且map的大小设置了100,jdk是21
jstack1
jstack2

@areyouok
Copy link
Collaborator

第一个图表示正在clean占据了锁,第二个图表示正在等clean完成(LinkedHashMapCache.java:94),如果没有第三个图揭示在等什么别的东西,那就还是map里面东西太多导致clean太慢了。

之前的程序在Cleaner类里面加了一把大锁,这里已经优化还未发布,不过和你给出的两个图无关。

另外,线程A(或多个线程)等线程B释放锁,而线程B执行较慢迟迟不释放,这不叫死锁。线程A持有锁1,线程B持有锁2,线程A等待锁2,线程B等待锁1,永远互相锁住,这才是死锁。

@areyouok
Copy link
Collaborator

这里或许还可以优化下,但是local cache太多的话还是不好哈,毕竟LinkedHashMapCache只是自己做的一个简单的local cache

@areyouok
Copy link
Collaborator

我猜到一个可能的原因,或许和这个提交有关,2.7.5和2.7.6会受到影响,你可以把版本改为2.7.4试试。
修复这个问题的新版本2.7.7最晚明天发布。

#820

@HetaoWangl
Copy link

第一个图表示正在clean占据了锁,第二个图表示正在等clean完成(LinkedHashMapCache.java:94),如果没有第三个图揭示在等什么别的东西,那就还是map里面东西太多导致clean太慢了。

之前的程序在Cleaner类里面加了一把大锁,这里已经优化还未发布,不过和你给出的两个图无关。

另外,线程A(或多个线程)等线程B释放锁,而线程B执行较慢迟迟不释放,这不叫死锁。线程A持有锁1,线程B持有锁2,线程A等待锁2,线程B等待锁1,永远互相锁住,这才是死锁。

image
感谢回复😄。我是通过jstack的线程持续时长简单分析判断clean线程在LinkedHashMapCache的迭代过程中可能存在类似死循环的问题(或者entry太多,遍历时间久,但是目前来看概率不大),导致锁无法释放,从而造成其他读线程无法获取到读锁,描述成死锁确实有问题。我测试的环境有5个节点,15左右的qps,本地缓存过期时间设置的2H,一天以内会复现,某一个节点会开始阻塞,因为我得恢复节点,所以没法观察最长会阻塞多久,以及会不会恢复,目前最长持续过12H+,之后通过重启强制中断了,整个过程其他节点正常运行。目前已经换成了caffeine,暂时平稳。

@areyouok
Copy link
Collaborator

问题是这样的,为了更好的适应java21,在这个修改里面把synchronized换成了ReentrantLock。进一步他使用了ReentrantReadWriteLock,本意是为了提升一点点性能。但之前他没注意,我也遗漏了,其实LinkedHashMap的读操作也是带有更新的,所以就可能会导致并发问题。

#820

我本想把这里重新写一下,但还是从简吧,马上就发布。

@HetaoWangl
Copy link

问题是这样的,为了更好的适应java21,在这个修改里面把synchronized换成了ReentrantLock。进一步他使用了ReentrantReadWriteLock,本意是为了提升一点点性能。但之前他没注意,我也遗漏了,其实LinkedHashMap的读操作也是带有更新的,所以就可能会导致并发问题。

#820

我本想把这里重新写一下,但还是从简吧,马上就发布。

感谢师兄😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants