邪恶八进制信息安全团队技术讨论组's Archiver

sunwear 2005-11-10 04:46

[转载]Java 线程锁定优化策略

<P>信息来源:<A href="http://flier_lu.blogone.net/"><FONT class=tt_title>Flier's Sky</FONT></A> (<A href="http://www.blogcn.com/User8/flier_lu/">[url]http://www.blogcn.com/User8/flier_lu/[/url]</A>)<BR><A href="http://www.briangoetz.com/pubs.html" target=_blank>BrianGoetz</A>最近在其dw<A href="http://www-128.ibm.com/developerworks/views/java/libraryview.jsp?search_by=practice:" target=_blank>Javatheoryandpractice专栏</A>里发表了几篇有趣的文章,介绍了SunHotSpotJVM在以后几个版本中,对锁定等性能优化的思路。<BR><BR><A href="http://www-128.ibm.com/developerworks/java/library/j-jtp10185/?ca=dgr-lnxw01Mustang-SO" target=_blank>SynchronizationoptimizationsinMustang</A><BR><BR>其中核心的思路是将锁分为contended和uncontended两类来分别优化。根据80/20经验原则和一些实测数据,大多数的线程锁实际上都只是预防性的。例如被广泛使用的Vector类中,绝大多数方法都被缺省加上synchronized修饰符以避免多线程问题,虽然这能够最大限度降低潜在的问题,但相应付出的代价也是巨大的。因为大量的实际使用都是局部或者不会涉及线程同步情况的,例如下面这种情况:
[code]
public String getStoogeNames() {
      Vector v = new Vector();
       v.add("Moe"[img]/images/wink.gif[/img];
       v.add("Larry"[img]/images/wink.gif[/img];
       v.add("Curly"[img]/images/wink.gif[/img];
       return v.toString();
  }
[/code]要缓解此问题,一方面可以从使用者角度通过库的选择来避免无效锁定,另一方面则可以由JVM根据行为进行锁定优化。<BR>对前者来说,随着JDK的发展可以说灵活性大大增强。例如选择性使用ArrayList和Collections.synchronizedList可以获得类似的能力,但并不用付出不必要的代价;同时也可以使用已成为JDK1.5标准库的concurrent库,根据多线程的使用情况进行锁定优化。这方面C/C++库的设计理念非常优秀,那就是决不为用不到的东西付出代价。<BR>对后者来说,则可以发挥JVM与传统静态编译相比最大的优势,根据运行时行为进行优化。<BR><BR>Lockelision的思路,就是通过escape分析找出根本不存在多线程引用可能性的锁。对这种情况Java语言规范中明确允许进行优化,直接去掉不必要的锁定语义。而最大的问题则在于如何进行escape分析,并找到锁的可优化范围。因为相对于基于栈的C/C++来说,Java内存模型目前是完全基于堆的,每个对象都在全局堆中可以由任何外部线程访问。<BR>有兴趣深入研究的话,可以看看下面这两篇文章<BR><BR><A href="http://www.research.ibm.com/people/g/gupta/toplas03.pdf" target=_blank><IMG alt=::URL:: hspace=2 src="http://www.blogcn.com/images/aurl.gif" align=absBottom border=0><A href="http://www.research.ibm.com/people/g/gupta/toplas03.pdf" target=_blank>[url]http://www.research.ibm.com/people/g/gupta/toplas03.pdf[/url]</A> </A><BR><BR><A href="http://www.research.ibm.com/people/j/jdchoi/escape-pointsto.html" target=_blank><IMG alt=::URL:: hspace=2 src="http://www.blogcn.com/images/aurl.gif" align=absBottom border=0><A href="http://www.research.ibm.com/people/j/jdchoi/escape-pointsto.html" target=_blank>[url]http://www.research.ibm.com/people/j/jdchoi/escape-pointsto.html[/url]</A> </A><BR><BR>Adaptivelocking的思路,则是将contended的锁定按锁定时间进一步细分。因为对大多数锁定来说,锁定的时间都是非常短的,例如很多get/set方法,以及简单的内存操作。对这些方法来说,如果要完成一个完整的锁定流程,需要涉及到对象锁状态更新、等待线程对象构建、甚至与操作系统一级的线程调度打交道。相对于实际要工作所耗费的时间来说,锁定这个操作自身消耗的资源可能反而是大头。对这些情况而言,与其建立一个完整的锁上下文,不如直接用spin锁机制进行等待:<BR><BR><BR><BR><BR><BR></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0><BR><BR><BR><BR>
<TBODY><BR><BR><BR><BR>
<TR><BR><BR><BR><BR>
<TD><BR><BR><BR><BR>
<TABLE class=ubb_code cellSpacing=0 cellPadding=6 width="95%" align=right border=0><BR><BR><BR><BR>
<TBODY><BR><BR><BR><BR>
<TR><BR><BR><BR><BR>
<TD><B>以下内容为程序代码:</B><BR><BR>while(lockStillInUse)<BR>;<BR></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>如果一定时间或尝试次数后还是无法获得锁,则JVM可以将此锁的类型转换为长时间的锁,然后构造完整的锁上下文进行管理。而这种优化方法,最能够体现动态JIT相对于传统静态编译器的优势,因为这些执行行为的信息是无法静态获取的。<BR><BR>Lockcoarsening的思路,则是通过将临近的几个相同锁定进行合并,以减少不必要的重复锁定操作。这种优化的原理是基于重复方法往往同时出现的模式,例如下面这种常见情况<BR><BR><BR><BR><BR><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0><BR><BR><BR><BR>
<TBODY><BR><BR><BR><BR>
<TR><BR><BR><BR><BR>
<TD><BR><BR><BR><BR>
<TABLE class=ubb_code cellSpacing=0 cellPadding=6 width="95%" align=right border=0><BR><BR><BR><BR>
<TBODY><BR><BR><BR><BR>
<TR><BR><BR><BR><BR>
<TD><B>以下内容为程序代码:
[code]public void addStooges(Vector v) {
       v.add("Moe"[img]/images/wink.gif[/img];
       v.add("Larry"[img]/images/wink.gif[/img];
       v.add("Curly"[img]/images/wink.gif[/img];
  }[/code]
对这种情况来说,每次调用add方法进行锁定和解锁是没必要的,JVM可以根据运行时信息选择性合并同类型锁。随着现在机器自动代码生成的广泛引用,可以预期这种基于行为对锁进行合并的思路会非常有用。<BR><BR>此外<A href="http://blogs.sun.com/roller/page/dagastine?entry=java_synchronization_optimizations_in_mustang" target=_blank>DavidDagastine</A>在其blog上也对Java同步锁优化进行了讨论<BR><BR>关于Escape分析<A href="http://www.briangoetz.com/pubs.html" target=_blank>BrianGoetz</A>还在另外一篇讨论Java性能问题误解的文章中有所提及。<BR><BR><A href="http://www-128.ibm.com/developerworks/java/library/j-jtp04223.html" target=_blank>Urbanperformancelegends</A><BR><A href="http://www-128.ibm.com/developerworks/java/library/j-jtp09275.html" target=_blank>Urbanperformancelegends,revisited</A><BR><BR>文中讨论了一些对Java性能问题的常见误解,其中很多问题是旧版本JVM和Java库中存在的,随着JDK的不断更新已经不同程度上得到缓解,例如下述等等问题。<BR><BR>Synchronizationisreallyslow<BR>Declaringclassesormethodsfinalmakesthemfaster<BR>Immutableobjectsarebadforperformance<BR><BR>这些问题的出现,往往是因为使用者对JVM的实现和优化思路不熟悉导致的。实际上HotSpot自从JDK1.3版本以后,实际上有了非常大的进步,无论是从功能还是性能上,都已经远远超出了某些人的预期。而在可以预见的Mustang和Dolphin中,更高级和动态的优化还会不断加入进来,并从JVM一级对应用产生透明的性能提升。<BR><BR>例如基于Escape分析的栈分配优化,有可能一改JVM只从堆中建立对象的传统,直接将动态分析得到的仅由线程自身使用的对象,以类似C/C++的方式直接在栈中分配。这一改进如果能够成熟,对JVM性能的提升将是非常可观的。因为对现代CPU架构来说,提升性能的一个关键问题就在于Cache的使用。相对于位置离散的堆来说,栈一般都能确保在缓存中的命中率;而在堆中分配内存,哪怕分配性能很高,但因为缓存往往无法命中,带来的性能缺失会非常大。而且使用基于栈的内存管理模式,还可以享受到析构对象的快捷性,函数调用完成后,只需要将原函数的栈顶指针弹出即可释放所有临时对象。<BR><BR>如果对此方面有兴趣,可以看看<A href="http://www.briangoetz.com/pubs.html" target=_blank>BrianGoetz</A>这个非常不错的<A href="http://www-128.ibm.com/developerworks/views/java/libraryview.jsp?search_by=practice:" target=_blank>Javatheoryandpractice专栏</A>,下面是其中关于性能优化的其他文章<BR><BR><A href="http://www-128.ibm.com/developerworks/java/library/j-jtp03253.html" target=_blank>Performancemanagement--doyouhaveaplan?</A><BR><A href="http://www-128.ibm.com/developerworks/java/library/j-jtp1029.html" target=_blank>Isthatyourfinalanswer?</A><BR><A href="http://www-128.ibm.com/developerworks/java/library/j-jtp02183.html" target=_blank>Tomutateornottomutate?</A>

页: [1]
© 1999-2008 EvilOctal Security Team