Java постоянная ссылка вызывает утечку памяти? - PullRequest
1 голос
/ 26 марта 2020

jvm jstat команда:

  jstat -gcutil 14378 2000 20
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT   
  8.04   0.00  87.61   9.06  96.45  94.34    702   13.804     8    2.521   16.325

Анализ памяти Eclipse Перейдите к блоку кода:

public class MicrosoftConstant {

/**
 * TODO TTS 请求头设置
 */
public static final List<Header> TTS_REQUEST_HEADERS = new ArrayList<Header>(){
    {
        add(new BasicHeader("Content-Type", "application/ssml+xml"));
        add(new BasicHeader("X-Microsoft-OutputFormat", "xxx"));
        add(new BasicHeader("X-Search-AppId", "xxx"));
        add(new BasicHeader("X-Search-ClientID", "xxx"));
        add(new BasicHeader("User-Agent", "xxx"));
        add(new BasicHeader("Accept", "*/*"));
    }
};

}

Константы с использованием блоков кода:

List<Header> headers = MicrosoftConstant.TTS_REQUEST_HEADERS;
    headers.add(new BasicHeader("Ocp-Apim-Subscription-Key", microsoftConfig.getAppKey()));
    headers.add(new BasicHeader("Authorization", "Bearer " + authToken));
    InputStream audioStream = null;

        HttpEntity httpEntity = httpApiService.doPost(microsoftConfig.getTtsUrl(), body.getBytes(), headers);

Интерфейс не освобождает память при большом количестве обращений。

Я не знаю, как решить эту проблему. Кто-нибудь может предложить решение?

Ответы [ 2 ]

1 голос
/ 26 марта 2020

У вас не только утечка памяти, но и возможная утечка безопасности. Проблема в том, что для каждого запроса вы добавляете заголовки в список TTS_REQUEST_HEADERS. Это означает, что с каждым запросом список увеличивается, и он никогда не сжимается.

Кроме того, вы используете 'шаблон двойной инициализации' , но в данном случае это не ' Это большая проблема.

Еще хуже, это означает, что фактический запрос может повторять определенные заголовки несколько раз (это зависит от того, что делает HTTP-клиент с повторными заголовками), что может непреднамеренно привести к утечке информации о предыдущих запросах. .

Решение этой проблемы состоит в том, чтобы скопировать список, добавить к копии заголовки вашего запроса, указав c, и использовать копию для выполнения запроса. Чтобы убедиться, что вы непреднамеренно не изменили список в константе, убедитесь, что он не является изменяемым (таким образом, он фактически является константой).

Для этого определите список как неизменяемый список, для пример:

public static final List<Header> TTS_REQUEST_HEADERS = 
        Collections.unmodifiableList(Arrays.asList(
                new BasicHeader("Content-Type", "application/ssml+xml"),
                new BasicHeader("X-Microsoft-OutputFormat", "xxx"),
                new BasicHeader("X-Search-AppId", "xxx"),
                new BasicHeader("X-Search-ClientID", "xxx"),
                new BasicHeader("User-Agent", "xxx"),
                new BasicHeader("Accept", "*/*")));

Или, для Java 9 и выше, используя List.of:

public static final List<Header> TTS_REQUEST_HEADERS = List.of(
        new BasicHeader("Content-Type", "application/ssml+xml"),
        new BasicHeader("X-Microsoft-OutputFormat", "xxx"),
        new BasicHeader("X-Search-AppId", "xxx"),
        new BasicHeader("X-Search-ClientID", "xxx"),
        new BasicHeader("User-Agent", "xxx"),
        new BasicHeader("Accept", "*/*"));

Ваш код запроса станет:

// Copy the standard list of headers for this request
List<Header> headers = new ArrayList<>(MicrosoftConstant.TTS_REQUEST_HEADERS);
headers.add(new BasicHeader("Ocp-Apim-Subscription-Key", microsoftConfig.getAppKey()));
headers.add(new BasicHeader("Authorization", "Bearer " + authToken));
InputStream audioStream = null;

HttpEntity httpEntity = httpApiService.doPost(microsoftConfig.getTtsUrl(), body.getBytes(), headers);
0 голосов
/ 26 марта 2020

Каждый раз, когда вы запускаете второй блок кода, TTS_REQUEST_HEADERS получает в нем еще два элемента. Это означает, что для каждого запроса добавляются еще два заголовка. Данные объекта TTS_REQUEST_HEADERS не являются постоянными с final (просто указатель на объект является окончательным и не может быть изменен, сам объект может измениться, список может увеличиться), а метод .add будет сделать его длиннее и длиннее.

Вы можете .clone TTS_REQUEST_HEADERS перед добавлением заголовков who для каждого указанного запроса c. Это мелкая копия, что означает, что BasicHeaders, добавленные вами в TTS_REQUEST_HEADERS изначально, будут использоваться повторно.

Возможно, вам придется изменить public static final List<Header> на public static final ArrayList<Header>, чтобы получить .clone в работа.

...