简体   繁体   English

调试Hibernate / Ehcache死锁

[英]Debugging Hibernate/Ehcache deadlock

We are using Ehcache version 2.4.4 with Hibernate 3.5.5-FINAL. 我们正在使用Ehcache版本2.4.4和Hibernate 3.5.5-FINAL。 I've got a strange case happening on my debug environment - it looks like Ehcache is getting into a deadlock. 我的调试环境发生了一个奇怪的情况 - 看起来Ehcache陷入了僵局。 Here's the relevant bit of the stack trace: 这是堆栈跟踪的相关位:

http-8080-2@7345 daemon, prio=5, in group 'main', status: 'WAIT'
      at sun.misc.Unsafe.park(Unsafe.java:-1)
      at java.util.concurrent.locks.LockSupport.park(LockSupport.java:158)
      at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:811)
      at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:842)
      at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1178)
      at java.util.concurrent.locks.ReentrantReadWriteLock$WriteLock.lock(ReentrantReadWriteLock.java:807)
      at net.sf.ehcache.store.compound.Segment.put(Segment.java:427)
      at net.sf.ehcache.store.compound.CompoundStore.put(CompoundStore.java:141)
      at net.sf.ehcache.Cache.putInternal(Cache.java:1434)
      at net.sf.ehcache.Cache.put(Cache.java:1367)
      at net.sf.ehcache.Cache.put(Cache.java:1339)
      at net.sf.ehcache.constructs.EhcacheDecoratorAdapter.put(EhcacheDecoratorAdapter.java:111)
      at net.sf.ehcache.hibernate.regions.EhcacheTransactionalDataRegion.put(EhcacheTransactionalDataRegion.java:127)
      at net.sf.ehcache.hibernate.strategy.NonStrictReadWriteEhcacheEntityRegionAccessStrategy.putFromLoad(NonStrictReadWriteEhcacheEntityRegionAccessStrategy.java:66)
      at net.sf.ehcache.hibernate.nonstop.NonstopAwareEntityRegionAccessStrategy.putFromLoad(NonstopAwareEntityRegionAccessStrategy.java:180)
      at org.hibernate.engine.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:180)
      at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:898)
      at org.hibernate.loader.Loader.doQuery(Loader.java:773)
      at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:270)
      at org.hibernate.loader.Loader.loadEntity(Loader.java:1953)
      at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:86)
      at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:76)
      at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3270)
      at org.hibernate.event.def.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:496)
      at org.hibernate.event.def.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:477)
      at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:227)
      at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:147)
      at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:1080)
      at org.hibernate.impl.SessionImpl.immediateLoad(SessionImpl.java:1018)
      at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:176)
      at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:215)
      at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:191)
      at vyre.content.items.ItemInfo_$$_javassist_87.equals(ItemInfo_$$_javassist_87.java:-1)
      at org.hibernate.util.EqualsHelper.equals(EqualsHelper.java:33)
      at org.hibernate.type.AbstractType.isEqual(AbstractType.java:132)
      at org.hibernate.type.ComponentType.isEqual(ComponentType.java:153)
      at org.hibernate.cache.CacheKey.equals(CacheKey.java:79)
      at net.sf.ehcache.store.compound.Segment.containsKey(Segment.java:279)
      at net.sf.ehcache.store.compound.CompoundStore.containsKey(CompoundStore.java:353)
      at net.sf.ehcache.store.compound.impl.MemoryOnlyStore.containsKeyInMemory(MemoryOnlyStore.java:121)
      at net.sf.ehcache.Cache.searchInStoreWithStats(Cache.java:1884)
      at net.sf.ehcache.Cache.get(Cache.java:1549)
      at net.sf.ehcache.constructs.EhcacheDecoratorAdapter.get(EhcacheDecoratorAdapter.java:75)
      at net.sf.ehcache.hibernate.regions.EhcacheTransactionalDataRegion.get(EhcacheTransactionalDataRegion.java:105)
      at net.sf.ehcache.hibernate.strategy.NonStrictReadWriteEhcacheEntityRegionAccessStrategy.get(NonStrictReadWriteEhcacheEntityRegionAccessStrategy.java:55)
      at net.sf.ehcache.hibernate.nonstop.NonstopAwareEntityRegionAccessStrategy.get(NonstopAwareEntityRegionAccessStrategy.java:122)
      at org.hibernate.event.def.DefaultLoadEventListener.loadFromSecondLevelCache(DefaultLoadEventListener.java:586)
      at org.hibernate.event.def.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:459)
      at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:227)
      at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:147)
      at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:1080)
      at org.hibernate.impl.SessionImpl.immediateLoad(SessionImpl.java:1018)
      at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:176)
      at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:215)
      at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:191)
      at vyre.content.items.Item_$$_javassist_102.getName(Item_$$_javassist_102.java:-1)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-1)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
      at java.lang.reflect.Method.invoke(Method.java:597)
      at org.apache.velocity.runtime.parser.node.PropertyExecutor.execute(PropertyExecutor.java:142)
      at org.apache.velocity.util.introspection.UberspectImpl$VelGetterImpl.invoke(UberspectImpl.java:533)
      at org.apache.velocity.runtime.parser.node.ASTIdentifier.execute(ASTIdentifier.java:198)
      at org.apache.velocity.runtime.parser.node.ASTReference.execute(ASTReference.java:252)
      at org.apache.velocity.runtime.parser.node.ASTReference.render(ASTReference.java:332)
      at org.apache.velocity.runtime.parser.node.SimpleNode.render(SimpleNode.java:336)
      at org.apache.velocity.runtime.RuntimeInstance.render(RuntimeInstance.java:1277)
      at org.apache.velocity.runtime.RuntimeInstance.evaluate(RuntimeInstance.java:1216)
      at org.apache.velocity.runtime.RuntimeInstance.evaluate(RuntimeInstance.java:1165)
      at org.apache.velocity.app.Velocity.evaluate(Velocity.java:191)
      at org.apache.jsp.WEB_002dINF.jsp.pub_005fmodule.taglibs.contentTemplate.search.itemLink_jsp._jspService(itemLink.jsp:36)

  (...another hundred or so irrelevant stack trace fragments skipped...)

This is how I understand the situation: 这就是我了解情况的方式:

  • the stack bottom begins where Apache Velocity is doing an evaluation and is passed a Hibernate proxy object 堆栈底部从Apache Velocity进行评估开始,并传递给Hibernate代理对象
  • this object is cached with <cache usage="nonstrict-read-write"/> and has a composite key 此对象使用<cache usage="nonstrict-read-write"/>缓存,并具有复合键
  • Hibernate attempts to get the entity from the cache Hibernate尝试从缓存中获取实体
  • Hibernate/Ehcache checks equalness of the object (note: the "real" equals method is never executed) Hibernate / Ehcache检查对象的相等性(注意:永远不会执行“real” equals方法)
  • the equal check returns false and the object is being loaded by the Hibernate 等号检查返回false,Hibernate正在加载对象
  • as soon as the load succeeds, the object is placed in the cache 加载成功后,对象就会被放入缓存中
  • deadlock? 僵局?

The offending code snippet of looks like as follows: 违规的代码片段如下所示:

net.sf.ehcache.store.compound.Segment.put(Segment.java:427) 

423 Element put(Object key, int hash, Element element, boolean onlyIfAbsent) { 
424    boolean installed = false; 
425    Object encoded = create(key, element); 
426 
427    writeLock().lock(); 
428    try { 
429       // ensure capacity 
430       if (count + 1 > threshold) { 

I can access the encoded object but it looks like writeLock() has been acquired already and therefore the whole thread is stuck. 我可以访问encoded对象,但看起来已经获取了writeLock() ,因此整个线程都被卡住了。 This is where my powers end as I know very little about internals of Ehcache Segment . 这是我的权力结束的地方,因为我对Ehcache Segment内部知之甚少。

Can anyone provide any tips on how to debug this further? 任何人都可以提供有关如何进一步调试的提示吗? Unfortunately, creating a little, self-consistent test case is not an option. 不幸的是,创建一个小的,自洽的测试用例不是一种选择。

This has also been posted on Ehcache forums page . 这也发布在Ehcache论坛页面上

Thanks in advance. 提前致谢。

Answering my own question just in case if someone else slips on this and the post in Ehcache forums will disappear. 回答我自己的问题以防万一其他人滑倒这个并且Ehcache论坛中的帖子将会消失。

The reason: 原因:
The reason for the deadlock was coming from the same thread, where an attempt to locate the object in the cache was done. 死锁的原因来自同一个线程,其中尝试在缓存中找到对象。 In one of the bottom parts of the stack, Ehcache was doing readLock().lock() and writeLock().lock() on the same lock object. 在堆栈的一个底部,Ehcache在同一个锁对象上执行readLock().lock()writeLock().lock() This is obviously a no-no. 这显然是禁忌。

Why did this happen? 为什么会这样? This happened because cache was loading another object as a side effect (another big no-no) and both objects ended in the same memory segment (and hence shared the same ReentrantLock ). 发生这种情况是因为缓存正在加载另一个对象作为副作用(另一个大禁忌)并且两个对象都在同一个内存段中结束(因此共享相同的ReentrantLock )。 Hint: I use the same cache region as I did not want to specify capacities for hundreds of different entity types. 提示:我使用相同的缓存区域,因为我不想为数百种不同的实体类型指定容量。

Why did this happen? 为什么会这样? Inadvertent loading on cache key lookup was happening because of my Hibernate mapping. 由于我的Hibernate映射,无意中加载了缓存键查找。 The object (which was being looked up) had a composite key, referring to another object. 对象(正在查找)有一个复合键,引用另一个对象。 Parts of that composite key have been used in the equals method and was causing that load. 该复合键的一部分已在equals方法中使用,并导致该负载。 As a coincidence, loaded object was also attempted to be placed in the same cache segment - and causing a deadlock. 巧合的是,还尝试将加载的对象放置在同一缓存段中 - 并导致死锁。

Lessons learned 得到教训
Be extra careful with your Hibernate mappings. 对Hibernate映射要格外小心。 If you have a composite key, never use <key-many-to-one as this can lead to unpredictable results. 如果您有复合键,请不要使用<key-many-to-one因为这会导致不可预测的结果。 I think many people do not realize this only because they place different types of objects into different cache regions, but inadvertent loads are nevertheless evil. 我想很多人没有意识到这一点,只是因为他们将不同类型的对象放入不同的缓存区域,但是无意中的负载仍然是邪恶的。

All credits go to Alex from Terracotta forums for helping me to find this out. 所有积分都来自Terracotta论坛的Alex,以帮助我找到这个。

You could use JProfiler (there's a fully functional evaluation version) to look at the current locking graph. 您可以使用JProfiler (有一个功能齐全的评估版本)来查看当前的锁定图。 It supports java.util.concurrent locks and will tell you who has the lock and where it was acquired. 它支持java.util.concurrent锁定,并会告诉您谁拥有锁定以及获取锁定的位置。 With that information it will be easier to analyze the problem. 有了这些信息,就可以更容易地分析问题。

Disclaimer: My company develops JProfiler 免责声明:我公司开发JProfiler

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM