Java常量引用导致内存泄漏?;

Java常量引用导致内存泄漏?;,java,memory-leaks,Java,Memory Leaks,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 MicrosoftC

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", "*/*"));
    }
};
公共类MicrosoftConstant{
/**
*待办事项请求头设置
*/
公共静态最终列表TTS_请求_头=新ArrayList(){
{
添加(新的BasicHeader(“内容类型”、“应用程序/ssml+xml”);
添加(新BasicHeader(“X-Microsoft-OutputFormat”、“xxx”);
添加(新BasicHeader(“X-Search-AppId”、“xxx”);
添加(新的BasicHeader(“X-Search-ClientID”、“xxx”);
添加(新BasicHeader(“用户代理”、“xxx”);
添加(新的BasicHeader(“接受”)、“*/*”);
}
};
}

使用代码块的常量:

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);
List headers=MicrosoftConstant.TTS\u请求\u头;
add(新的BasicHeader(“Ocp Apim订阅密钥”,microsoftConfig.getAppKey());
添加(新的BasicHeader(“授权”、“承载人”+authToken));
InputStream audioStream=null;
HttpEntity HttpEntity=httpApiService.doPost(microsoftConfig.gettsurl(),body.getBytes(),headers);
在大量访问期间,接口不会释放内存


我不知道如何解决这个问题。有人能提供解决方案吗?

每次运行第二个代码块时,
TTS\u请求\u头
会在其中再获得两个项目。这意味着每个请求都会添加两个以上的头。
TTS\u REQUEST\u HEADERS
对象的数据与
final
不一致(只是指向对象的指针是final,不能更改,对象本身可以更改,列表可以增长),而
.add
方法将使其越来越长

在为每个特定请求添加who标头之前,您可以
.clone
克隆
TTS\U请求\U标头。它是一个浅拷贝,这意味着您最初在
TTS\u请求\u头中添加的基本阅读器将被重新使用


可能您必须将
公共静态最终列表
更改为
公共静态最终数组列表
,才能使
克隆
正常工作。

您不仅存在内存泄漏,还可能存在安全泄漏。问题是,对于您执行的每个请求,您都将头添加到列表
TTS\u请求\u头
。这意味着随着每个请求,列表都会增加,而不会缩小

此外,您还可以使用,但在本例中,这并不是什么大问题

更糟糕的是,这意味着实际请求可能会多次重复某些头(这取决于HTTP客户端对重复头的处理),这可能会无意中泄漏有关以前请求的信息

此问题的解决方案是复制列表,将特定于请求的头添加到副本中,然后使用副本执行请求。为了确保不会无意中修改常量中的列表,请确保它不可修改(这样它实际上是一个常量)

为此,请将列表定义为不可修改的列表,例如:

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", "*/*")));
您的请求代码将变成:

// 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);
//复制此请求的标准头列表
列表头=新的ArrayList(MicrosoftConstant.TTS\u请求\u头);
add(新的BasicHeader(“Ocp Apim订阅密钥”,microsoftConfig.getAppKey());
添加(新的BasicHeader(“授权”、“承载人”+authToken));
InputStream audioStream=null;
HttpEntity HttpEntity=httpApiService.doPost(microsoftConfig.gettsurl(),body.getBytes(),headers);

这是一个比我下面建议的更好的解决方案。我对新的不可修改列表集合函数不太熟悉。@claj
Collections.unmodifiableList
包装现有列表并禁止通过该包装进行修改(因此不能通过包装修改基础列表,但可以由直接访问该列表的程序部分修改)。Java 9的
List.of
生成一个不可变的列表。使用
clone()
不是一个好主意,使用
new ArrayList()
更好。
TTS\u请求\u头不应该是可修改的,因此集合需要是不可变的(或者至少不能通过此引用进行修改)。克隆一个不可变的列表将导致一个不可变的列表,这将阻止添加新条目。它的实现确实创建了一个新的ArrayList,并将指向原始列表中项目的指针放在新列表中。无法修改原始列表。但是如果您更改原始列表引用的对象,这是不好的…是的,克隆可以在
ArrayList
中正常工作,但是您希望防止修改常量中的列表(否则它将不是常量),以防止像OP现在这样的编程错误。防止修改会禁止克隆(或者至少会使克隆变得不太有用,因为不可修改列表的克隆(如果可能的话)也应该是不可修改的)。必须克隆常量列表的约定实际上是一个糟糕的约定。不可变列表更好!
// 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);