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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions