Java基础、中级、高级、架构面试资料

记一次 JDK 底层 TLS 锁竞争性能问题

JAVA herman 240浏览
公告:“业余草”微信公众号提供免费CSDN下载服务(只下Java资源),关注业余草微信公众号,添加作者微信:xttblog2,发送下载链接帮助你免费下载!
本博客日IP超过2000,PV 3000 左右,急需赞助商。
极客时间所有课程通过我的二维码购买后返现24元微信红包,请加博主新的微信号:xttblog2,之前的微信号好友位已满,备注:返现
受密码保护的文章请关注“业余草”公众号,回复关键字“0”获得密码
所有面试题(java、前端、数据库、springboot等)一网打尽,请关注文末小程序
视频教程免费领
腾讯云】1核2G5M轻量应用服务器50元首年,高性价比,助您轻松上云

这个标题原本有些长,“Thread.State: BLOCKED (on object monitor) at java.security.Provider.getService(Provider.java:680)”。我嫌它不利于搜索,所以给它改了。

这个问题,几乎是必现的。但是能遇到这个问题的人,不多。我在网上找了非常多的资料,都没有很好的解决办法。我这里分享一些思路,给遇到的朋友们。

"pool-2-thread-41001881" prio=10 tid=0x00007fc5a809a800 nid=0x2e80 waiting for monitor entry [0x00007fc560e5a000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at java.security.Provider.getService(Provider.java:680)
	- locked <0x00000007531b5040> (a sun.security.provider.Sun)
	at sun.security.jca.ProviderList.getService(ProviderList.java:331)
	at sun.security.jca.GetInstance.getInstance(GetInstance.java:157)
	at java.security.Security.getImpl(Security.java:695)
	at java.security.MessageDigest.getInstance(MessageDigest.java:167)

这个问题的表现是,线程 BLOCKED。然后继续追踪 locked 的对象,你会发现,很多线程都在等待持有这个 locked 的对象。

"pool-2-thread-41001879" prio=10 tid=0x00007fc5a81ea000 nid=0x2e7e waiting for monitor entry [0x00007fc567da4000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at java.security.Provider.getService(Provider.java:680)
	- waiting to lock <0x00000007531b5040> (a sun.security.provider.Sun)
	at sun.security.jca.ProviderList.getService(ProviderList.java:331)
	at sun.security.jca.GetInstance.getInstance(GetInstance.java:157)
	at java.security.Security.getImpl(Security.java:695)
	at java.security.MessageDigest.getInstance(MessageDigest.java:167)

然后,我通过工具,分享堆栈信息。https://fastthread.io/ft-error.jsp

结果表面,没有任何的死锁。只是有大量的线程处在 waiting for monitor entry 的状态。

一切的证据都指向java.security.Provider.getService 这个方法。查看其中的源码,确实有一个锁竞争synchronized

但是,网上基本上没有很多的解决办法。只是,很早之前,就有人反馈这里有性能问题,还给 jdk 提交了 bug,https://github.com/Backblaze/b2-sdk-java/issues/45。但是,官方不太承认,而且认为这个问题没那么严重,就这样一拖就是好几年。

官方的意思是,虽然这里有锁,存在性能问题,但是你们不能这样用。你们应该把一些东西缓存起来。话虽如此说,但是官方自有的类库中也没有对 getService 这个方法的耗时久的对象缓存起来。

反正,官方的意思就是,这个问题不紧急,出现概率低。好几年后,才在去年的 Java8 中的某个版本中修复了。利用了 ConcurrentHashMap 来解决并发问题。

官方说 JDK8 大于等于 8u241 以上的都被修复了,https://bugs.java.com/bugdatabase/view_bug.do?bug_id=7092821

但是经过我的验证并没有。我试最新的 8u401 的才有修复的代码。

在某些不便升级的场景,可以自行封装一部分底层 api。具体的做法,可以使用 ThreadLocal,或者是使用 ConcurrentHashMap。但是对于第三方 jar 包里的,就不好一一封装了。

更多疑难问题,参见我的知识星球https://t.zsxq.com/19IiYqST8

参考资料

业余草公众号

最后,欢迎关注我的个人微信公众号:业余草(yyucao)!可加作者微信号:xttblog2。备注:“1”,添加博主微信拉你进微信群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作也可添加作者微信进行联系!

本文原文出处:业余草: » 记一次 JDK 底层 TLS 锁竞争性能问题