2011-10-18 108 views
4

我们在Hibernate 3.5.5-FINAL中使用Ehcache版本2.4.4。我的调试环境出现了一个奇怪的情况 - 看起来Ehcache正在陷入僵局。下面是堆栈跟踪的相关位:调试休眠/ Ehcache死锁

[email protected] 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...) 

这就是我所理解的情况:

  • 栈底开始,在Apache的速度做一个评估,并传递一个Hibernate代理对象
  • 此对象缓存与<cache usage="nonstrict-read-write"/>和有复合键
  • 休眠试图从缓存
  • 休眠/的Ehcache CHEC得到实体对象的KS equalness(注:从不执行的“真实” equals方法)
  • 相等检查返回false和物体正在由休眠
  • 只要负载成功,则对象被放置在装载缓存
  • 死锁?

看起来有问题的代码片段就像如下:

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) { 

我可以访问encoded对象,但它看起来像writeLock()已经被收购,因此整个线程被卡住。这是我的力量结束的地方,因为我对Ehcache Segment的内部知之甚少。

任何人都可以提供关于如何进一步调试的提示吗?不幸的是,创建一个小的,自我一致的测试用例不是一种选择。

这也已张贴在Ehcache forums page

在此先感谢。

+0

你可以使用jvisualvm来检查锁何时出现 - 它应该给你更多的想法。顺便说一下你在哪个平台上运行?前段时间我在AIX上遇到了类似的问题(所有线程都被阻塞,没有阻塞线程的证据),事实证明这是由于缺少补丁 – SirVaulterScoff

回答

7

回答我的问题,以防万一,如果有人滑倒在这一点,并在论坛上的Ehcache后就会消失。

原因:
的原因僵局在同一个线程,在那里做定位在缓存中的对象的尝试来。在堆栈的底部部分中,Ehcache在同一个锁对象上执行了readLock().lock()writeLock().lock()。这显然是一个禁忌。

为什么会发生这种情况? 发生这种情况是因为缓存加载另一个对象作为副作用(另一个大的禁止否),并且两个对象都在相同的内存段中结束(因此共享相同的ReentrantLock)。提示:我使用相同的缓存区域,因为我不想为数百种不同的实体类型指定容量。

为什么会发生这种情况? 由于我的Hibernate映射,无意中加载了缓存键查找。该对象(正在查找)有一个复合键,指的是另一个对象。 equals方法中已使用该组合键的部分内容,并导致该负载。巧合的是,加载的对象也试图放置在同一个缓存段中 - 并导致死锁。

教训
额外小心你的Hibernate映射。如果您有复合密钥,请勿使用<key-many-to-one,因为这可能会导致不可预知的结果。我想很多人并没有意识到这一点,因为他们将不同类型的对象放置在不同的缓存区域,但无意中的加载却是邪恶的。

所有积分从兵马俑论坛转到亚历克斯帮助我找到答案。

+0

为什么显然是一个不行的readLock()。lock()和writeLock()。lock()?作家不必同时获得读取锁定和写入锁定来阻止读者和其他作者? – morten

+0

我没有得到发生了什么事。我看到类似的问题,但没有Hibernate。我看到一个缓存的方法正在调用另一个,这看起来很奇怪,但我不明白它是如何导致死锁的。加载另一个对象时出现了什么问题?它是如何造成僵局的? – morten

+1

那么,这是6或7年前,所以我不能记住它的细节(也不能再访问源代码),不管它们是不同的线程。 https://stackoverflow.com/a/37087262/7345表明,对于不同的线程,这肯定会导致一个问题。 – mindas

0

您可以使用JProfiler(有一个功能完整的评估版本)来查看当前的锁定图形。它支持java.util.concurrent锁,并会告诉你谁有锁以及它在哪里被获取。有了这些信息,分析问题就会变得更加容易。

免责声明:我公司开发的JProfiler

+0

感谢kingo,但在这种情况下,调试器比探查器更合适。我发现Ehcache先获取读锁,然后尝试获取写锁。问题是为什么Hibernate要用这种方式编排它...... – mindas