简体   繁体   中英

Memory leak, spring 3.2.0.RELEASE, httpcomponents 4.2.3

Using spring 3.2.0.RELEASE resttemplate & httpcomponents 4.2.3 to make rest calls. Memory footprint is steadily increasing until it reaches max.

Following is the configuration:

<bean id="myRestTemplate" class="org.springframework.web.client.RestTemplate">
    <constructor-arg>
        <bean class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory">
            <constructor-arg index="0">
                <bean factory-bean="httpClient" factory-method="get"/>
            </constructor-arg>
        </bean>
    </constructor-arg>
</bean>
<bean id="httpClient" class="com.mycompany.myproject.common.rest.HttpClient">
    <constructor-arg index="0" ref="myKeyserverCA" ></constructor-arg>
    <constructor-arg index="1" value="${com.mycompany.myproject.security.client.keyPassword}" ></constructor-arg>
    <constructor-arg index="2" value="${default.max.total.connections}" ></constructor-arg>
    <constructor-arg index="3" value="${default.max.host.connections}" ></constructor-arg>
</bean>
<bean id="myKeyserverCA"
      class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
    <property name="location" value="classpath:${com.mycompany.myproject.security.client.keyStore}" />
    <property name="password" value="${com.mycompany.myproject.security.client.keyStorePass}" />
</bean>    

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;
}



}    

After running load tests on this component, we see lot of SSLSocketImpl objects and byte[]. Incoming references to SSLSocketImpl are from Finalizer object.

When we do netstat on the machine after load test is stopped, we dont see any open tcp connections to underlying services. During the load test however, there are many many connections in TIME_WAIT state and few in ESTABLISHED state but all of them are closed after test is stopped.

Are we missing any API call to close sockets? Why do we have so many SSLSocketImpl objects hanging around in our heap?

Bit late reply but this answer is for future visitors to this question. The reason that you see large volume of SSLSocketImpl in your heap and then which gets garbage collected is because SSLSessionContext supports caching SSL connections, which can be reused by across distinct TCP connections to reduce handshake overhead when negotiating temporary encryption keys after the actual TCP connection has been established.

By default the SSLSessionContext unlimited number of sessions for default time period of 24 hours, which means that you can end up using lot of heap memory under high traffic. One way is to set cache size that suits your application needs. Here is an example:

sslContext.getServerSessionContext().setSessionCacheSize(1000);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM