2016-09-28 27 views
5

我已經分析了大型應用程序中最近幾天發生的類加載器泄漏問題,並且已經解決了這個問題。由於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(請參閱「環境重現」下方),您可以通過以下這個重現步驟:

  1. 部署的戰爭演示項目(A)
  2. 重載它(B)
  3. 重載它再次(C)
  4. 觸發GC(d)
  5. 取消部署
  6. 特里格呃GC(E)
  7. 見的元空間被完全地沒有清理(F)

enter image description here

我已經創建一個堆轉儲和GC的最短路徑是這樣的:

enter image description here

這是一樣在我的大量應用。 所提到的解決方法(一點點的啓發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) 

我的解決方法現在解決了這個問題,但當然這只是一種解決方法。

我想知道,也許有人能回答我的問題的一個或多個:

  1. 這是一個「錯誤」在SolrJ,HttpClient的,Java或我的應用程序?
  2. 如果是我的應用程序,我做錯了什麼?
  3. 如果它不是我的應用程序,它是一個已知的問題?我找不到任何關於此的信息。 (哪裏)我應該創建一個錯誤票?
  4. 爲什麼會有「無效」證書? (順便說一句:也許泄漏也將解決,如果我從信任商店刪除此證書...我沒有測試,但我認爲一個無效或損壞的證書不應該導致一個類加載程序泄漏...)
  5. 已任何人有關於此的更多信息?我不能相信我是唯一一個能夠檢測到這種行爲的人(除了這是我的應用...見我的問題2)。

最後但並非最不重要,我的環境重現:

  • Tomcat的版本:的Apache Tomcat/8.0.14(Debian的)
  • JVM版本:1.8.0_91-b14的
  • JVM供應商:Oracle公司
  • 操作系統名稱:Linux的
  • OS版本:3.16.0-4-AMD64
  • 硬件架構:
+0

的4.2.1.7對於那些誰想要重現該問題:我已經增加了有關Solr的服務器和密鑰存儲到'README一些信息。md'在GitHub上:https://github.com/CptS/solrj-classloader-leak#environment –

+0

更新:似乎Mattias Jiderhamn分享我的意見,這是一個Java錯誤︰https://github.com/mjiderhamn/classloader-防泄漏/問題/ 58#issuecomment-252438440 –

+0

聽起來不像JDK的錯誤。需要更多信息進行進一步評估。如果密鑰庫中的證書有效,GC行爲是否可重現?爲了提高性能,SSLContext可以在靜態字段中緩存一些可信的證書。內存使用可能會在第一次使用SSLContext時增加。如果多次部署/取消部署,行爲如何?例如,執行1-7/A-F 10次。 –

回答

0

爲什麼會出現「無效的」證書

在密鑰存儲在證書的「發行人備用名稱」擴展值是空的,這不符合X.509規範。見4.2.1.6和RFC 5280

$ keytool -exportcert ... -file ... 
$ keytool -printcert -v -file ... 
... 
#10: ObjectId: 2.5.29.18 Criticality=false 
Unparseable IssuerAlternativeName extension due to 
java.io.IOException: No data available in passed DER encoded value. 

0000: 30 00            0. 
+0

是的,我明白這一點。 Sry,我的問題被誤解了。這是一個全新安裝並且乾淨的debian,它有一個全新安裝的oracle jvm和一個(也是全新安裝的)tomcat。我沒有自己安裝任何證書。所以無效證書來自分佈式軟件包。這讓我感到驚訝,這就是我對這個問題的意思。 –

相關問題