内存泄漏,弹簧3.2.0.RELEASE,httpcomponents 4.2.3

使用spring 3.2.0.RELEASE resttemplate和httpcomponents 4.2.3进行rest调用。 内存占用量稳步增长,直到达到最大值。

以下是配置:

                   

HttpClient的:

 import org.apache.http.conn.scheme.PlainSocketFactory; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.conn.PoolingClientConnectionManager; import org.apache.http.params.CoreConnectionPNames; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import java.security.KeyStore; public class HttpClient { private static final int DEFAULT_READ_TIMEOUT_MILLISECONDS = (60 * 1000); private KeyStore keystore; private String password; private int MAX_TOTAL_CONNECTION; private int MAX_PER_ROUTE; public HttpClient(KeyStore keyStore, String keyPassword, int MAX_TOTAL_CONNECTION, int MAX_PER_ROUTE) { this.keystore = keyStore; this.password = keyPassword; this.MAX_TOTAL_CONNECTION = MAX_TOTAL_CONNECTION; this.MAX_PER_ROUTE = MAX_PER_ROUTE; } public org.apache.http.client.HttpClient get() { PoolingClientConnectionManager connectionManager = new PoolingClientConnectionManager(getSchemeRegistry(this.keystore, this.password)); connectionManager.setMaxTotal(MAX_TOTAL_CONNECTION); connectionManager.setDefaultMaxPerRoute(MAX_PER_ROUTE); connectionManager.closeExpiredConnections(); org.apache.http.client.HttpClient httpClient = new DefaultHttpClient(connectionManager); httpClient.getParams().setIntParameter(CoreConnectionPNames.SO_TIMEOUT, DEFAULT_READ_TIMEOUT_MILLISECONDS); return httpClient; } private static SchemeRegistry getSchemeRegistry(KeyStore keyStore, String keyPassword) { SchemeRegistry registry = new SchemeRegistry(); try{ TrustManager[] trustManagerArray = { new TautologicalX509TrustManager() }; KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(keyStore, keyPassword.toCharArray()); SSLContext sslc = SSLContext.getInstance("TLS"); sslc.init(kmf.getKeyManagers(), trustManagerArray, null); SSLSocketFactory sslSocketFactory = new SSLSocketFactory(sslc, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); registry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory())); registry.register(new Scheme("https", 443, sslSocketFactory)); return registry; }catch(Exception e){ throw new RuntimeException(e.getMessage()); } } } 

TautologicalX509TrustManager:

 import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.X509TrustManager; public class TautologicalX509TrustManager implements X509TrustManager { private static final X509Certificate[] EMPTY_CERTIFICATES = new X509Certificate [0]; public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { } public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { } public X509Certificate[] getAcceptedIssuers() { return EMPTY_CERTIFICATES; } } 

在对此组件运行负载测试后,我们看到很多SSLSocketImpl对象和byte []。 SSLSocketImpl的传入引用来自Finalizer对象。

当我们在负载测试停止后在机器上执行netstat时,我们没有看到任何打开到底层服务的tcp连接。 然而,在负载测试期间,有许多处于TIME_WAIT状态的连接,很少处于ESTABLISHED状态,但是在测试停止后它们都被关闭。

我们是否缺少关闭套接字的API调用? 为什么我们堆中有这么多SSLSocketImpl对象?

有点迟到的回复,但这个答案是针对这个问题的未来访问者。 您在堆中看到大量SSLSocketImpl然后收集垃圾的原因是因为SSLSessionContext支持缓存SSL连接,这可以跨不同的TCP连接重用,以减少在实际TCP连接之后协商临时加密密钥时的握手开销成立了。

默认情况下, SSLSessionContext会话的默认时间为24小时,这意味着您可以在高流量下使用大量堆内存。 一种方法是设置适合您的应用程序需求的缓存大小。 这是一个例子:

sslContext.getServerSessionContext().setSessionCacheSize(1000);