簡體   English   中英

不活動后調用java.beans.Introspector.getBeanInfo時的性能問題

[英]Performance issues when calling java.beans.Introspector.getBeanInfo after inactivity

我正在使用第3方庫,該庫動態創建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.BeanProxygetPropertyDescriptorCacheEntry方法中的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.<init>(Unknown Source)
    at java.util.jar.JarFile.<init>(Unknown Source)
    at java.util.jar.JarFile.<init>(Unknown 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.<init>(Unknown Source)
    at java.beans.Introspector.getBeanInfo(Unknown Source)
    - locked <0x181bed70> (a java.lang.Object)

我忍不住想,有一種JRE / JVM jar緩存將在一個小時后到期,並迫使對jar文件進行重新掃描,但是我在網上找不到任何概述此類行為的信息。

編輯:事實證明,Tomcat WebappClassLoader確實緩存了JAR文件並定期清除該緩存。 現在,找出該緩存是否仍可配置...

編輯:Tomcat在上次訪問jar文件后90秒關閉了所有JAR文件。 關閉jar文件時,我WebappClassLoaderWebappClassLoader以進行打印。 關閉jar文件后,我嘗試重現延遲,但無法恢復。 因此,這告訴我要么是JRE / JVM jar文件緩存,要么是操作系統(或我的機器,防病毒等)中固有的東西,這些東西導致長時間的延遲后導致加載時間變慢。 仍在努力中...

在OSGi環境中, java.beans.Introspector可能是一個真正的問題,在OSGi環境中,搜索不存在的類可能特別昂貴。 由於伙伴類的加載,在Equinox中尤其如此: Eclipse-BuddyPolicy: depenentEclipse-BuddyPolicy: global會導致嚴重的性能問題。

java.beans.Introspector用於少數意外的地方

  • org.apache.log4j.config.PropertySetter
  • org.springframework.beans.CachedIntrospectionResults
  • org.hibernate.util.Cloneable#copyListeners

通常,當指定Introspector.IGNORE_ALL_BEANINFO標志時, Introspector.IGNORE_ALL_BEANINFO在Equinox中應該相對安全。 不會,因為在當前的Oracle JVM實現中,除非指定了Introspector.USE_ALL_BEANINFO,否則不使用BeanInfo緩存。 顯然,這與javadocs直接沖突,因此我想說這實際上是Introspector實現(或文檔)中的錯誤。

在我的雇主處,我們也嚴重依賴動態生成的類。 考慮到Introspector的問題及其行為方式(例如,依賴ClassLoader行為),並嘗試加載許多我們沒有的*BeanInfo類,這對我們來說是不行的,我們決定不使用Introspector並重新實現功能靠我們自己。

不確定您真正需要多少BeanInfo,但是使用反射和自定義屬性-元數據信息組件可能會更容易,您可以更好地控制它們。 而且,更容易地說,我還表示,一旦將應用程序部署到其他應用程序服務器上就可以安心,它們具有自己的ClassLoader行為,並且比Tomcat上的行為更沉重且不可配置。

BeanInfo和相關組件是接口,因此您甚至只需要重新實現Introspector本身,而不是重新使用它的所有代碼。

如前所述,默認情況下,Tomcat中的WebAppClassLoader JAR文件緩存90秒。 90秒后調用Introspector.getBeanInfo ,我確實確認Tomcat正在重新加載JAR文件。 幾分鍾不活動后,延遲很小,但是仍然存在延遲。 我從來沒有確定為什么在一個小時的不活動之后,延遲時間如此之長。

最終,我的解決方案是重寫WebAppClassLoader並無限期地緩存JAR。 在我們的方案中,這是完全可以接受的,因為我們將Tomcat包裝在我們自己的應用程序中,沒有其他Web應用程序共享同一個Tomcat實例,並且我們不允許自動重新加載Web應用程序。 如果您正在考慮實施類似的解決方案,請記住這一點。

這是重寫JAR緩存行為的代碼( cacheJarFiles是我添加到WebAppClassLoader類的自定義布爾值):

private static boolean cacheJarFiles = true;

...

public void closeJARs(boolean force) {
   if (cacheJarFiles) {
      return;
   }
   if (jarFiles.length > 0) {
      synchronized (jarFiles) {
         if (force || (System.currentTimeMillis() 

...

}

本質上,我正在中止對closeJARs的調用。 我們現在看到的性能提升是相當可觀的。 閑置一個小時后,我們對Introspector.getBeanInfo新調用節省了10-60秒鍾以上的時間。 幾分鍾不活動后,我們節省了100-200毫秒。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM