<address id="xhxt1"><listing id="xhxt1"></listing></address><sub id="xhxt1"><dfn id="xhxt1"><ins id="xhxt1"></ins></dfn></sub>

    <thead id="xhxt1"><dfn id="xhxt1"><ins id="xhxt1"></ins></dfn></thead>

    ClassNotFoundException: 真的会使你的JVM慢下来吗?

    本文翻译自javacodegeeks? ?作者Pierre Hugues Charbonneau? ?译者:TonySpark 校对:郑旭东
    大多数Java开发者比较熟悉这个普通的?java.lang.ClassNotFoundException。这个问题的根源逐渐被开发人员所了解(在ClassPath中找不到相关类或者类库,类加载器委托问题等等),然而它对于整个JVM及性能的影响却鲜为人知。这个异?;嵊τ贸绦虻南煊κ奔浜涂衫┱剐杂泻艽蟮挠跋?。

    ?在部署了多个应用程序的大型Java EE企业系统中,由于运行期间有很多不同的类加载器,所以这种类型的问题出现的最多的。也就增加了面对未检测的ClassNotFoundException的风险,除非定义了明确的业务影响和实现了很好的日志监控,否则JVM类加载IO操作和线程锁竞争将会持续的影响应用程序的性能。

    下文中的程序将演示你的客户生产系统中任何ClassNotFoundException都应认真对待并及时解决。

    Java类Loading: 优化性能缺失的环节

    只有正确的理解JAVA类加载模型才能正确的理解性能问题。ClassNotFoundException 本质上意味着JVM定位或通过下面的方法加载类是失败的:

    1)Class.forName()方法

    2)ClassLoader.findSystemClass() 方法

    3)ClassLoader.loadClass()方法。

    在JVM的生命周期中,应用程序中的类只会发生一次(当然也有动态重新部署功能),同时一些应用程序也依赖动态类加载操作。

    然而,重复的成功或者失败的类加载操作是相当的惹人烦,尤其是试图使用JDK中?java.lang.ClassLoader 来进行加载操作。实际上,由于向后兼容性,在JDK1.7+ 除非类加载器被明确标记为具有并行能力(”parallel capable”)否则默认只会一次加载一个类。请记住即使在类的级别发生同步,一个重复的类加载失败还会根据你所处理的Java线程头发级别触发线程锁竞争。这种情况如果在JDK1.6中,当类加载实例级别进行同步时变得更加严重。

    classNotFoundException
    classNotFoundim2
    因为这个原因,像JBoss WildFly 8??这样的Java EE容器会使用他们自身的并发类加载器来加载你的应用程序类。这此类加载器在更精细的粒度上实现了锁,因此可以并发的从同一个类加载器实例来加载不同的类。这同样与最新的JDK1.7+中改善性的支持多线程定制类加载器(?Multithreaded Custom Class Loaders?)保持一致。这种多线程定制类加载器可以一定程度上阻止类加载器死锁现象。 话虽然是这么说的。像java.* 还有Java EE容器??檎庋低臣侗鸬睦?,他们的类加载器还依赖于JDK默认的类加载器。这就意味着重复的类加载失败仍然会触发严重的线程锁竞争。这恰恰是下文我们要重现和演示的。

    线程锁竞争– 问题复制

    我们按照以下规范创建了一个简单应有程序,来重现和模拟这个问题

    • JAX-RS(REST)Web 服务采用一个假的类名“located”从系统包级别执
    String className =”java.lang.WrongClassName”;
    Class.forName(className);
    

    这次模拟是采用20个JAX-RS Web service 线程来并发执行。 每一次调用都会有一个ClassNotFoundException. 为了减少对IO影响,我们禁用日志,并将关注点只放在类加载竟争上。

    现在我们来看看JvisualVM中运行了30-60秒的结果。我们可以清晰的看到大量的BLOCKED线程等待在Object monitor 上获取锁。

    minotor

    分析JVM线程dump,可以清晰的暴露出问题:线程锁竞争。我们可以从JBoss将类的加载委托给JDK的ClassLoader的堆栈跟踪中看到。 为什么呢? 这是因为我们的错误的Java 类名被认为是系统类path的一部份。在这种情况下,JBoss将会把加载委托给系统类加载器,触发了针对那个特定类名的系统级同步,同时来自其它线程的waiters 等待获取一个锁来加载同样的类名。

    许多线程等待获取 LOCK 0x00000000ab84c0c8…

    "default task-15" prio=6 tid=0x0000000014849800 nid=0x2050 waiting for monitor entry [0x000000001009d000]
    java.lang.Thread.State: BLOCKED (on object monitor)
     at java.lang.ClassLoader.loadClass(ClassLoader.java:403)
     - waiting to lock <0x00000000ab84c0c8> (a java.lang.Object)
     // Waiting to acquire a LOCK held by Thread “default task-20”
     at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
     at java.lang.ClassLoader.loadClass(ClassLoader.java:356) // JBoss now delegates to system ClassLoader..
     at org.jboss.modules.ConcurrentClassLoader.performLoadClass(ConcurrentClassLoader.java:371)
     at org.jboss.modules.ConcurrentClassLoader.loadClass(ConcurrentClassLoader.java:119)
     at java.lang.Class.forName0(Native Method)
     at java.lang.Class.forName(Class.java:186)
     at org.jboss.tools.examples.rest.MemberResourceRESTService.SystemCLFailure(MemberResourceRESTService.java:176)
     at org.jboss.tools.examples.rest.MemberResourceRESTService$Proxy$_$$_WeldClientProxy.SystemCLFailure(Unknown Source)
     at sun.reflect.GeneratedMethodAccessor15.invoke(Unknown Source)
     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
     at java.lang.reflect.Method.invoke(Method.java:601)
    ……………………..
    

    罪魁祸首的线程– default task-20

    "default task-20" prio=6 tid=0x000000000e3a3000 nid=0x21d8 runnable [0x0000000010e7d000]
       java.lang.Thread.State: RUNNABLE
                   at java.lang.Throwable.fillInStackTrace(Native Method)
                   at java.lang.Throwable.fillInStackTrace(Throwable.java:782)
                   - locked <0x00000000a09585c8> (a java.lang.ClassNotFoundException)
                   at java.lang.Throwable.<init>(Throwable.java:287)
                   at java.lang.Exception.<init>(Exception.java:84)
                   at java.lang.ReflectiveOperationException.<init>(ReflectiveOperationException.java:75)
    at java.lang.ClassNotFoundException.<init>(ClassNotFoundException.java:82) // ClassNotFoundException!                                      at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
                   at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
                   at java.security.AccessController.doPrivileged(Native Method)
                   at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
                   at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
                   - locked <0x00000000ab84c0e0> (a java.lang.Object)
                   at java.lang.ClassLoader.loadClass(ClassLoader.java:410)
    - locked <0x00000000ab84c0c8> (a java.lang.Object)   // java.lang.ClassLoader: LOCK acquired                                                             at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
                   at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
                   at org.jboss.modules.ConcurrentClassLoader.performLoadClass(ConcurrentClassLoader.java:371)
                   at org.jboss.modules.ConcurrentClassLoader.loadClass(ConcurrentClassLoader.java:119)
                   at java.lang.Class.forName0(Native Method)
                   at java.lang.Class.forName(Class.java:186)
                   at org.jboss.tools.examples.rest.MemberResourceRESTService.SystemCLFailure(MemberResourceRESTService.java:176)
                   at org.jboss.tools.examples.rest.MemberResourceRESTService$Proxy$_$$_WeldClientProxy.SystemCLFailure(Unknown Source)
    …………………………………
    

    现在我们通过一个被标记为 “application”包中的一部分的Java 类为替换我们的类名,并在同样的条件下重新测试。

    String className =”org.ph.WrongClassName”;
    Class.forName(className);
    

    test
    正如我们所看到,不需要再应对BLOCKED线程.. 为什么这样呢?咱们一块看看JVM线程dump,更好的理解一下这种行为的变化。

    "default task-51" prio=6 tid=0x000000000dd33000 nid=0x200c runnable [0x000000001d76d000]
       java.lang.Thread.State: RUNNABLE
                   at java.io.WinNTFileSystem.getBooleanAttributes(Native Method)    // IO overhead due to JAR file search operation
                   at java.io.File.exists(File.java:772)
                   at org.jboss.vfs.spi.RootFileSystem.exists(RootFileSystem.java:99)
                   at org.jboss.vfs.VirtualFile.exists(VirtualFile.java:192)
                   at org.jboss.as.server.deployment.module.VFSResourceLoader$2.run(VFSResourceLoader.java:127)
                   at org.jboss.as.server.deployment.module.VFSResourceLoader$2.run(VFSResourceLoader.java:124)
                   at java.security.AccessController.doPrivileged(Native Method)
                   at org.jboss.as.server.deployment.module.VFSResourceLoader.getClassSpec(VFSResourceLoader.java:124)
                   at org.jboss.modules.ModuleClassLoader.loadClassLocal(ModuleClassLoader.java:252)
                   at org.jboss.modules.ModuleClassLoader$1.loadClassLocal(ModuleClassLoader.java:76)
                   at org.jboss.modules.Module.loadModuleClass(Module.java:526)
                   at org.jboss.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:189)   // JBoss now fully responsible to load the class
                   at org.jboss.modules.ConcurrentClassLoader.performLoadClassUnchecked(ConcurrentClassLoader.java:444) // Unchecked since using JDK 1.7 e.g. tagged as “safe” JDK
                   at org.jboss.modules.ConcurrentClassLoader.performLoadClassChecked(ConcurrentClassLoader.java:432)
                   at org.jboss.modules.ConcurrentClassLoader.performLoadClass(ConcurrentClassLoader.java:374)
                   at org.jboss.modules.ConcurrentClassLoader.loadClass(ConcurrentClassLoader.java:119)
                   at java.lang.Class.forName0(Native Method)
                   at java.lang.Class.forName(Class.java:186)
                   at org.jboss.tools.examples.rest.MemberResourceRESTService.AppCLFailure(MemberResourceRESTService.java:196)
                   at org.jboss.tools.examples.rest.MemberResourceRESTService$Proxy$_$$_WeldClientProxy.AppCLFailure(Unknown Source)
                   at sun.reflect.GeneratedMethodAccessor60.invoke(Unknown Source)
    ……………….
    

    上述堆栈跟踪信息表明:

    • 自从Java类名不再作为Java系统包的一部分,就不会有ClassLoader的委托,因此也不会有同步操作。
    • 自从JBoss认为JDK1.7+是个“安全”的JDK. ConcurrentClassLoader .使用LoadClassUnchecked()来实现 , 不会触发任何对象监控锁(Object monitor lock).
    • 没有同步就意味着不存在因为不间断ClassNotFoundException错误而导致的线程锁竞争。

    注意在这种情况下JBoss做了大量工作来阻止线程锁竟争,由于过多的JAR文件查找操作和IO开销,重复的类加载尝试将一定程度上降低性能。要解决这样的问题需立即采取纠正措施。

    结束语

    我希望你喜欢这篇文章并对因为 过度的类加载操作而导致潜在的性能影响有进一步的理解。当JDK1.7 和现在的JAVA EE容器针对像死锁和线程锁竞争这样类加载问题上做出很大的提升时,潜在的问题仍然存在。因此,我强烈建议您密切监控你的应用程序运行情况、日志,并及时改正像java.lang.ClassNotFoundException?和java.lang.NoClassDefFoundError?这样的类加载错误.

    原创文章,转载请注明: 转载自并发编程网 – www.gofansmi6.com本文链接地址: ClassNotFoundException: 真的会使你的JVM慢下来吗?


    FavoriteLoading添加本文到我的收藏
    • Trackback 关闭
    • 评论 (0)
    1. 暂无评论

    您必须 登陆 后才能发表评论

    return top

    爱投彩票 hjt| 6pf| bz6| jzh| v6l| dfn| 6xr| dt6| vlb| p7b| ztt| 5tf| rx5| np5| lbb| j5p| pff| 5bj| tl6| zbx| n6d| bvb| 4fh| bl4| pxn| z4j| z4j| vbl| 4tl| bdd| 5jr| xn5| xnd| j5v| dnv| 3rp| nz3| nxf| t4p| b4n| rfp| 4tl| vl4| nxx| v4j| bvh| 2jz| nn3| fzj| n3v| jbr| 3px| tnf| tp3| bnf| r3j| fdh| 4dt| dff| 2dt|