![](/img/trans.png)
[英]Java.beans.Introspector.getBeanInfo() fails to assign writeMethods
[英]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.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.<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文件時,我WebappClassLoader
了WebappClassLoader
以進行打印。 關閉jar文件后,我嘗試重現延遲,但無法恢復。 因此,這告訴我要么是JRE / JVM jar文件緩存,要么是操作系統(或我的機器,防病毒等)中固有的東西,這些東西導致長時間的延遲后導致加載時間變慢。 仍在努力中...
在OSGi環境中, java.beans.Introspector
可能是一個真正的問題,在OSGi環境中,搜索不存在的類可能特別昂貴。 由於伙伴類的加載,在Equinox中尤其如此: Eclipse-BuddyPolicy: depenent
和Eclipse-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.