Skip to content

Memory leak in HttpClientFactory due to GetHashOfMerchantConfiguration always producing unique hash per ApiClient #233

@anhnt88

Description

@anhnt88

We have identified a memory leak in cybersource-rest-client-java SDK, caused by the way HttpClientFactory caches OkHttpClient instances.

Each time a new ApiClient is created, a new HttpClientFactoryAdditionalSettings is instantiated:

// ApiClient class
.........
private HttpClientFactoryAdditionalSettings additionalSettings = new HttpClientFactoryAdditionalSettings();
.........

public ApiClient() {
		.....
		additionalSettings.setCustomRetryOnConnectionFailure(true);
		additionalSettings.setCustomRetryInterceptor(new RetryInterceptor(this.apiRequestMetrics));
		additionalSettings.setCustomNetworkEventListener(new NetworkEventListener(this.getNewRandomId(), System.nanoTime()));
		.........

This additionalSettings object contributes to the hash generated in GetHashOfMerchantConfiguration:

// HttpClientFactory

private static int GetHashOfMerchantConfiguration(MerchantConfig merchantConfig, HttpClientFactoryAdditionalSettings additionalSettings) {
    return Objects.hash(
        merchantConfig.getUserDefinedConnectionTimeout(),
        merchantConfig.getUserDefinedReadTimeout(),
        merchantConfig.getUserDefinedWriteTimeout(),
        merchantConfig.getUserDefinedKeepAliveDuration(),
        merchantConfig.getUserDefinedMaxIdleConnections(),
        additionalSettings.getCustomLoggingInterceptor(),
        additionalSettings.getCustomRetryInterceptor(),
        additionalSettings.getCustomSSLSocketFactory(),
        additionalSettings.getCustomX509TrustManager(),
        additionalSettings.getCustomHostnameVerifier(),
        additionalSettings.getCustomRetryOnConnectionFailure(),
        additionalSettings.getCustomNetworkEventListener(),
        additionalSettings.getCustomProxy(),
        additionalSettings.getCustomProxyAuthenticator()
    );
}
  • Because additionalSettings is a new object for each ApiClient, the hash is effectively always unique.

  • _httpClientInstances.computeIfAbsent(hash, ...) therefore creates a new OkHttpClient every time and stores it in the static map.

  • These OkHttpClient instances are never garbage collected, leading to memory leak.

Impact:

  • Heap usage increases over time.
  • Can eventually lead to OutOfMemoryError or exhaustion of file descriptors in long-running applications.

Reproduction steps:

  1. Create a new ApiClient instance:
ApiClient apiClient = new ApiClient();
MerchantConfig merchantConfig = new MerchantConfig(merchantProp);
apiClient.merchantConfig = merchantConfig;

CreatePaymentRequest requestObj = new CreatePaymentRequest();
PaymentsApi apiInstance = new PaymentsApi(apiClient);
result = apiInstance.createPayment(requestObj);
  1. Repeat the multiple times.
  2. Observe heap growth and accumulation of OkHttpClient instances in _httpClientInstances.

References

  • SDK version: 0.0.84

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions