Java 使用log4j显示响应的堆内存问题
我的代码中有一个Java 使用log4j显示响应的堆内存问题,java,out-of-memory,heap-memory,Java,Out Of Memory,Heap Memory,我的代码中有一个log4j记录器,其级别设置为INFO,如下所示: String response = ""; response = someRemoteAPICall(); LOGGER.info("Response: "+response); //line 3 所以在代码的第3行,我得到了带有OutOfMemory错误的大型堆转储 是否可以仅通过显示日志消息来获取OOM 我的转储分析显示以下浅堆和保留堆: 这个问题只能通过注释掉日志语句或设置调试级别而不是信息或其他优雅的方式来解决吗
log4j
记录器,其级别设置为INFO,如下所示:
String response = "";
response = someRemoteAPICall();
LOGGER.info("Response: "+response); //line 3
所以在代码的第3行,我得到了带有OutOfMemory错误的大型堆转储
是否可以仅通过显示日志消息来获取OOM
我的转储分析显示以下浅堆
和保留堆
:
这个问题只能通过注释掉日志语句或设置调试级别而不是信息或其他优雅的方式来解决吗
在这里我迷路了
更新:
BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
String inputLine;
response = new StringBuffer();
while ((inputLine = in.readLine()) != null) { //OOM
response.append(inputLine);
}
in.close();
对于要读取的大数据,上述代码中也发生了同样的情况
这是显示此代码相对于浅堆的保留堆的图像:
我的系统属性:
BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
String inputLine;
response = new StringBuffer();
while ((inputLine = in.readLine()) != null) { //OOM
response.append(inputLine);
}
in.close();
java.runtime.name-java(TM)SE运行时环境
java.runtime.version-1.8.0_51-b16
java.vm.specification.name-java虚拟机规范
java.vm.specification.vendor-Oracle公司
java.vm.specification.version-1.8
java.vm.name-java热点(TM)64位服务器vm
os.arch-amd64
os.name-Linux
os.version-3.13.0-61-generic
也许你不应该放弃你的全部回复。可能会导致66MB的字符串数据吗
哦,甚至一句简单的话,比如
Object o = new Object();
如果内存完全满,可能会导致内存溢出。哦,是的,可能会
在日志行中使用+进行字符串连接。这意味着,在日志发生之前,将创建一个带有新字符数组的新字符串
如果您的对象的toString()
碰巧使用了类似的功能,那么您可能最终得到的不是一个而是多个char[]副本,而char[]的副本一开始并不太小
如果你一定要把整件事都打印出来,你最好用
LOGGER.info("Response: %s", response);
它仅在日志信息实际启用时输出字符串
如果您知道您的文件很大,并且希望将其保持不变,则可以执行以下操作:
if(LOGGER.isInfoEnabled()) {
File dump = writeToDumpFile(response);
LOGGER.info("Response written to %s", dump.getAbsolutePath());
}
(并创建+执行原因的writeToDumpFile(响应))
更新您的更新
你不知道队伍要排多久
相反,请尝试:
InputStreamReaderin = new InputStreamReader(con.getInputStream());
response = new StringBuilder();
char[] buffer = new byte[1024];
int len = in.read(buffer);
while(len > 0 ) {
response.append(buffer, 0, len);
len = in.read(buffer);
}
in.close();
使用readLine()再次创建了整个字符串的副本:-)
如果您只想转储部分响应,您可以在第一次读取后立即执行…那么,我应该删除该日志语句,还是简单地将日志记录级别更改为调试以便进行测试?那么,我应该删除该日志语句,还是简单地将日志记录级别更改为调试以便进行测试?请检查您的响应。toString()-那里可能也有隐藏的宝藏。我在您的代码中遇到编译时错误:类型类别中的方法
info(Object,Throwable)
不适用于参数(String,String)。哦,好吧。对于带有的记录器,响应?我猜您必须执行,response.toString())
,在这种情况下,检查外部日志级别将是明智的做法,以便只调用它是绝对必要的。response已经是一个字符串变量,那么为什么要执行response.toString()
?另外,当打印响应的级别已经是INFO时,检查LOGGer.IsInfo()的条件的目的是什么?如果响应可能很大,则在INFO级别记录整个响应通常是不明智的。好的,但至少我希望保留该响应,然后解析它并将其发送到某个第三方服务器。将其保持在StringBuffer/StringBuilder/String
会导致OOM。