jackson实现的LRUMap(LinkedHashMap + ReentrantReadWriteLock)引起的死循环问题
- 线上服无法登录,通过监控发现有几个逻辑cpu占用都到了100%
- 和腾讯线上运维联系,确认Java下某个逻辑线程CPU占用一直占用100%,并确认了线程堆栈(top + jstack)
- 确认线程堆栈是jackson#LRUMap.put#HashMap.resize#LinkedHashMap.transfer
- 这是一个经典的hashmap#死循环问题
- 但上述只有在并发条件下才会出现问题,但是LRUMap#put是用了ReentrantReadWriteLock#写锁,应该是单线程的
- 原因#其根结在于LinkedHashMap的get()方法会改变数据链表
- 参考
- 结合之前的issues
- 看之前线上出问题的jstack
- 发现所有的逻辑线程都阻塞在LRUMap.get,这是一个ReentrantReadWriteLock#读锁
- 从读写锁原理来看,肯定是线程在持有写锁,结合刚才1的分析
- 查看了一下堆栈,发现有逻辑线程在执行#LRUMap.put,触发死循环,从而一直持有写锁,这样其他逻辑都被阻塞了,卡住
- 参考
- 解决
- LinkedHashMap + ReentrantReadWriteLock 实现LRUCache是有性能问题的,一个写操作会锁住整个缓存,阻塞所有读操作
- 其根结在于LinkedHashMap的get()方法会改变数据链表,结合读写锁(get),则会造成死循环
- 升级了jackson的版本到2.9.8,并且阅读了源代码发现LRUMap实现不再继承LinkedHashMap,而是采用组合了ConcurrentHashMap,而且不再使用读写锁
- jackson本身的相关issues