我已經分析了大型應用程序中最近幾天發生的類加載器泄漏問題,並且已經解決了這個問題。由於SolrJ,HttpClient,JVM或我的應用程序中的無效SSL證書導致類加載器泄漏?
我的應用程序使用SolrJ將通過一個@Bean
- 方法來initialilzed:
@Bean(destroyMethod = "close")
public SolrClient solrClient() {
return new HttpSolrClient(SOLR_URL);
}
SolrJ(org.apache.solr:solr-solrj:5.4.1
)使用了Apache的HttpClient(org.apache.httpcomponents:httpclient:4.4.1
)。 HttpClient使用普通的java類來初始化SSL上下文,如javax.net.ssl.SSLSocketFactory
。 通過這種方式,java加載trustManager並分析所有可信證書。如果出現錯誤,則證書(sun.security.x509.X509CertImpl
的一個實例)存儲在列表中,並通過拋出的異常得到豐富。 這個異常被吞下,我的應用程序仍然不知情。
據我所看到的,SSL上下文是在System /根類加載器,我的應用程序是在專用WebappClassLoader
因爲現在是包含在引用的SSL上下文內的IOException
這是問題堆棧跟蹤,回溯等到我的應用程序中的類。
但現在我不知道這是從哪裏來的。它是SolrJ客戶端,Apache HttpClient,Java本身(JVM)還是它是我的應用程序?
我已經做了一個小應用程序來重現您可以在這裏找到的問題:https://github.com/CptS/solrj-classloader-leak 這也包含一個解決方法(關閉掛鉤,刪除導致類加載器泄漏的引用)。
如果禁用關閉掛鉤(例如,通過註釋掉它),並開始一個乾淨的Tomcat(請參閱「環境重現」下方),您可以通過以下這個重現步驟:
- 部署的戰爭演示項目(A)
- 重載它(B)
- 重載它再次(C)
- 觸發GC(d)
- 取消部署
- 特里格呃GC(E)
- 見的元空間被完全地沒有清理(F)
我已經創建一個堆轉儲和GC的最短路徑是這樣的:
這是一樣在我的大量應用。 所提到的解決方法(一點點的啓發https://github.com/mjiderhamn/classloader-leak-prevention,但遺憾的是沒有解決我的問題)通過使用反射這些unparseableExtensions
和搜索刪除通過這樣的方式存儲在why
領域的例外: SSLContextImpl.DefaultSSLContext#defaultImpl
- >SSLContextImpl#trustManager
- >X509TrustManager#trustedCerts
- >X509CertImpl#info
- >X509CertInfo#extensions
- >CertificateExtensions#unparseableExtensions
- >UnparseableExtension#why
通過這樣做,我已經得到了異常的堆棧跟蹤,如果它可以幫助別人:
java.io.IOException: No data available in passed DER encoded value.
at sun.security.x509.GeneralNames.<init>(GeneralNames.java:61)
at sun.security.x509.IssuerAlternativeNameExtension.<init>(IssuerAlternativeNameExtension.java:136)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at sun.security.x509.CertificateExtensions.parseExtension(CertificateExtensions.java:113)
at sun.security.x509.CertificateExtensions.init(CertificateExtensions.java:88)
at sun.security.x509.CertificateExtensions.<init>(CertificateExtensions.java:78)
at sun.security.x509.X509CertInfo.parse(X509CertInfo.java:702)
at sun.security.x509.X509CertInfo.<init>(X509CertInfo.java:167)
at sun.security.x509.X509CertImpl.parse(X509CertImpl.java:1804)
at sun.security.x509.X509CertImpl.<init>(X509CertImpl.java:195)
at sun.security.provider.X509Factory.engineGenerateCertificate(X509Factory.java:100)
at java.security.cert.CertificateFactory.generateCertificate(CertificateFactory.java:339)
at sun.security.provider.JavaKeyStore.engineLoad(JavaKeyStore.java:755)
at sun.security.provider.JavaKeyStore$JKS.engineLoad(JavaKeyStore.java:56)
at sun.security.provider.KeyStoreDelegator.engineLoad(KeyStoreDelegator.java:224)
at sun.security.provider.JavaKeyStore$DualFormatJKS.engineLoad(JavaKeyStore.java:70)
at java.security.KeyStore.load(KeyStore.java:1445)
at sun.security.ssl.TrustManagerFactoryImpl.getCacertsKeyStore(TrustManagerFactoryImpl.java:226)
at sun.security.ssl.SSLContextImpl$DefaultSSLContext.getDefaultTrustManager(SSLContextImpl.java:767)
at sun.security.ssl.SSLContextImpl$DefaultSSLContext.<init>(SSLContextImpl.java:733)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at java.security.Provider$Service.newInstance(Provider.java:1595)
at sun.security.jca.GetInstance.getInstance(GetInstance.java:236)
at sun.security.jca.GetInstance.getInstance(GetInstance.java:164)
at javax.net.ssl.SSLContext.getInstance(SSLContext.java:156)
at javax.net.ssl.SSLContext.getDefault(SSLContext.java:96)
at javax.net.ssl.SSLSocketFactory.getDefault(SSLSocketFactory.java:122)
at org.apache.http.conn.ssl.SSLSocketFactory.getSystemSocketFactory(SSLSocketFactory.java:190)
at org.apache.http.impl.conn.SchemeRegistryFactory.createSystemDefault(SchemeRegistryFactory.java:85)
at org.apache.http.impl.client.SystemDefaultHttpClient.createClientConnectionManager(SystemDefaultHttpClient.java:121)
at org.apache.http.impl.client.AbstractHttpClient.getConnectionManager(AbstractHttpClient.java:484)
at org.apache.solr.client.solrj.impl.HttpClientUtil.setMaxConnections(HttpClientUtil.java:234)
at org.apache.solr.client.solrj.impl.HttpClientConfigurer.configure(HttpClientConfigurer.java:40)
at org.apache.solr.client.solrj.impl.HttpClientUtil.configureClient(HttpClientUtil.java:149)
at org.apache.solr.client.solrj.impl.HttpClientUtil.createClient(HttpClientUtil.java:125)
at org.apache.solr.client.solrj.impl.HttpSolrClient.<init>(HttpSolrClient.java:189)
at org.apache.solr.client.solrj.impl.HttpSolrClient.<init>(HttpSolrClient.java:162)
at de.test.spring.SolrJConfig.solrClient(SolrJConfig.java:20)
at de.test.spring.SolrJConfig$$EnhancerBySpringCGLIB$$dbd4362f.CGLIB$solrClient$0(<generated>)
at de.test.spring.SolrJConfig$$EnhancerBySpringCGLIB$$dbd4362f$$FastClassBySpringCGLIB$$8e7566a6.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:309)
at de.test.spring.SolrJConfig$$EnhancerBySpringCGLIB$$dbd4362f.solrClient(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:162)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1119)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1014)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:755)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
at de.test.WicketApplication.init(WicketApplication.java:32)
at org.apache.wicket.Application.initApplication(Application.java:950)
at org.apache.wicket.protocol.http.WicketFilter.init(WicketFilter.java:429)
at org.apache.wicket.protocol.http.WicketFilter.init(WicketFilter.java:353)
at org.apache.catalina.core.ApplicationFilterConfig.initFilter(ApplicationFilterConfig.java:279)
at org.apache.catalina.core.ApplicationFilterConfig.getFilter(ApplicationFilterConfig.java:260)
at org.apache.catalina.core.ApplicationFilterConfig.<init>(ApplicationFilterConfig.java:105)
at org.apache.catalina.core.StandardContext.filterStart(StandardContext.java:4640)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5247)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:724)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:700)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:714)
at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:919)
at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1703)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
我的解決方法現在解決了這個問題,但當然這只是一種解決方法。
我想知道,也許有人能回答我的問題的一個或多個:
- 這是一個「錯誤」在SolrJ,HttpClient的,Java或我的應用程序?
- 如果是我的應用程序,我做錯了什麼?
- 如果它不是我的應用程序,它是一個已知的問題?我找不到任何關於此的信息。 (哪裏)我應該創建一個錯誤票?
- 爲什麼會有「無效」證書? (順便說一句:也許泄漏也將解決,如果我從信任商店刪除此證書...我沒有測試,但我認爲一個無效或損壞的證書不應該導致一個類加載程序泄漏...)
- 已任何人有關於此的更多信息?我不能相信我是唯一一個能夠檢測到這種行爲的人(除了這是我的應用...見我的問題2)。
最後但並非最不重要,我的環境重現:
- Tomcat的版本:的Apache Tomcat/8.0.14(Debian的)
- JVM版本:1.8.0_91-b14的
- JVM供應商:Oracle公司
- 操作系統名稱:Linux的
- OS版本:3.16.0-4-AMD64
- 硬件架構:
的4.2.1.7對於那些誰想要重現該問題:我已經增加了有關Solr的服務器和密鑰存儲到'README一些信息。md'在GitHub上:https://github.com/CptS/solrj-classloader-leak#environment –
更新:似乎Mattias Jiderhamn分享我的意見,這是一個Java錯誤︰https://github.com/mjiderhamn/classloader-防泄漏/問題/ 58#issuecomment-252438440 –
聽起來不像JDK的錯誤。需要更多信息進行進一步評估。如果密鑰庫中的證書有效,GC行爲是否可重現?爲了提高性能,SSLContext可以在靜態字段中緩存一些可信的證書。內存使用可能會在第一次使用SSLContext時增加。如果多次部署/取消部署,行爲如何?例如,執行1-7/A-F 10次。 –