不活动后调用java.beans.Introspector.getBeanInfo时的性能问题

我正在使用第三方库,它动态创建Java类的实例,并在Introspector.getBeanInfo的帮助下填充这些实例.某些请求可能导致对Introspector.getBeanInfo的5或6次连续调用.我发现,当应用程序空闲大约一个小时左右时,第一次调用Introspector.getBeanInfo需要相当长的时间来执行(20-60秒)与后续调用(<100毫秒).在接下来的几分钟内进行的通话继续采取< 100毫秒,但是当我再等一小时时,第一次通话需要20-60秒. 为了尝试使用简单的测试应用程序重新创建行为,当java应用程序本身未运行一小时时,我发现了类似的行为.例如,如果我运行以下控制台应用程序,则可能需要15毫秒才能完成.如果我再等一个小时重新运行应用程序,则需要20秒才能完成.

long start = System.currentTimeMillis();
System.out.println("Start");
Introspector.getBeanInfo(MyClass.class,Object.class);
long end = System.currentTimeMillis();
System.out.println("End: " + (end-start));

我原本以为这个问题可能与Introspector类试图根据我的应用程序中不存在的标准命名约定(例如,MyClassBeanInfo)创建类的实例有关,并且需要很长时间来扫描尝试查找这些类的jar文件(我的java应用程序有超过100个引用的jar文件),但我使用反射调用了Introspector.getBeanInfo(MyClass.class,Object.class,Introspector.IGnorE_ALL_BEANINFO)(它是一个私有方法)在Sun的JRE中,通过查看代码似乎跳过BeanInfo类的查找),我仍然能够重现延迟.

我还搜索了有关任何类型的JRE / JVM jar缓存的信息,但还没有找到任何似乎可以解释这种行为的东西.任何人都有任何线索为什么这样做会如此,如果有什么我可以做的来解决它?

作为旁注,我在Windows XP上使用JDK 1.6.0_21.我使用的第三方库是BlazeDS.我的应用程序使用Spring / BlazeDS集成托管在Tomcat中.我覆盖了许多BlazeDS类,以精确确定延迟的位置(这是对flex.messaging.io.BeanProxy的getPropertyDescriptorCacheEntry方法中的Introspector.getBeanInfo的调用).此外,BlazeDS会缓存BeanInfo,因此只有在Blaze对映射到尚未处理的java类的对象进行反序列化时才会调用Introspector.getBeanInfo.所以,我确实有其他方法可以解决这个问题,但我真的想知道这种行为是否有一个有效的解释.

编辑:
我在处理过程中多次运行jstack同时重现问题(感谢@Tom)并确认它与加载jar文件有关.我在20秒的时间范围内延迟了5次线程(延迟的总时间),每次产生以下结果:

"http-8080-exec-6" daemon prio=6 tid=0x65cae800 nid=0x1a50 runnable [0x67a3d000]
   java.lang.Thread.State: RUNNABLE
    at java.util.zip.ZipFile.open(Native Method)
    at java.util.zip.ZipFile.Nown Source)
    at java.util.jar.JarFile.Nown Source)
    at java.util.jar.JarFile.Nown Source)
    at org.apache.catalina.loader.WebappClassLoader.openJARs(WebappClassLoader.java:2704)
    at org.apache.catalina.loader.WebappClassLoader.findResourceInternal(WebappClassLoader.java:2945)
    - locked <0x1804cc18> (a [Ljava.util.jar.JarFile;)
    at org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLoader.java:2739)
    at org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:1144)
    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1639)
    - locked <0x1803dd38> (a org.apache.catalina.loader.WebappClassLoader)
    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1517)
    at java.beans.Introspector.instantiate(UnkNown Source)
    at java.beans.Introspector.findExplicitBeanInfo(UnkNown Source)
    - locked <0x434649a0> (a java.lang.class for java.beans.Introspector)
    at java.beans.Introspector.Nown Source)
    at java.beans.Introspector.getBeanInfo(UnkNown Source)
    - locked <0x181bed70> (a java.lang.Object)

我不禁想到有一种JRE / JVM jar缓存在一小时后过期并强制重新扫描jar文件,但我找不到任何在线概述这种行为的东西.

编辑:
事实证明,Tomcat WebappClassLoader会缓存JAR文件并定期清除该缓存.现在来了解该缓存是否可以配置…

编辑:
在上次访问jar文件90秒后,Tomcat会关闭所有JAR文件.我覆盖了WebappClassLoader以在jar文件关闭时打印出来. jar文件关闭后,我试图重现延迟,但无法.因此,这告诉我有一个JRE / JVM jar文件缓存,或者只是操作系统(或我的机器,防病毒等)固有的东西,这些东西在长时间延迟后导致加载时间变慢.还在努力……

最佳答案
在我的雇主,我们也非常依赖动态生成的类.由于Introspector的问题及其行为(例如依赖于ClassLoader行为)并尝试加载我们没有的许多* BeanInfo类,对我们来说这是不行的,我们决定不使用Introspector并重新实现功能靠我们自己.

不确定你真正需要多少BeanInfo,但是使用反射和本土属性 – 元数据信息组件可能更容易,你可以更好地控制它.更容易,我还意味着,一旦您在其他应用程序服务器上部署应用程序,您就会安全,这些应用程序服务器具有自己的ClassLoader行为,并且比Tomcat上的行为更加繁重且不可配置.

BeanInfo和相关组件是接口,因此您甚至可能只需要重新实现Introspector本身而不是所有使用它的代码.

相关文章

应用场景 C端用户提交工单、工单创建完成之后、会发布一条工...
线程类,设置有一个公共资源 package cn.org.chris.concurre...
Java中的数字(带有0前缀和字符串)
在Java 9中使用JLink的目的是什么?
Java Stream API Filter(过滤器)
在Java中找到正数和负数数组元素的数量