我在下面的代码中做错了什么?或者netty4组件是否存在一些已知问题,它只有很高的内存使用率?
我的问题:
我使用Camel的netty4组件从套接字流式传输数据,聚合它然后再发送它。
我已经尝试了许多不同的策略来聚合数据,似乎没有任何帮助或损害内存使用。
我的聚合时间为30秒,并且在30秒内收到的数据总计约为1.3MB。
但是,我注意到我的内存使用量每30秒增加 4MB 。我在Linux中使用watch free -m
来监控内存消耗。除了运行Camel进程的终端之外,没有其他进程在前台运行。在运行Camel进程之前,内存使用率是完全稳定的(MB的规模没有波动)。
我使用Camel文档提供的几乎所有netty4设置,对我来说很明显,似乎没有什么能减少使用的内存量。
我使用
从命令行运行Camel实例java -Xms200M -Xmx275M -Xss512k -Drolling_log_dir=/logs/ -jar myCamel.jar
我的路线:
from( netty4:tcp://localhost:12345?clientMode=true&textline=true ).routeId( routeId + "A" )
.log( LoggingLevel.INFO, rollingLogFile, "${body}" )
.aggregate( constant(true), new StringAggregationStrategy(dataType) )
.completionInterval( 30000 )
.to( fileUri );
from( fileUri ).routeId( routeId + "B" )
.process(doTheThing)
.to( pushFile )
.log( "Transferred ${file:name} complete" );
StringAggregationStrategy.java:
package com.aggregators;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import org.apache.camel.Exchange;
import org.apache.camel.processor.aggregate.AggregationStrategy;
public class StringAggregationStrategy implements AggregationStrategy {
private static Path tempFileDir;
public StringAggregationStrategy(String dataType){
tempFileDir = Paths.get("camelTempAggFileStorage/" + dataType + "/");
}
public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
String newBody = newExchange.getIn().getBody(String.class);
String exchangeId;
Path tempAggFilePath;
if (!Files.exists(tempFileDir)){
try {
Files.createDirectories(tempFileDir);
} catch (IOException ex) {
ex.printStackTrace();
}
}
if (oldExchange == null){
cleanDirectory(tempFileDir);
exchangeId = newExchange.getExchangeId();
tempAggFilePath = Paths.get(tempFileDir.toString() + "/" + exchangeId + ".txt");
} else{
File oldFile = oldExchange.getIn().getBody(File.class);
tempAggFilePath = oldFile.toPath();
}
try (BufferedWriter writer = Files.newBufferedWriter(tempAggFilePath, StandardOpenOption.APPEND, StandardOpenOption.CREATE)){
if (oldExchange == null) {
writer.write(newBody);
newExchange.getIn().setBody(tempAggFilePath.toFile());
return newExchange;
} else {
writer.newLine();
writer.write(newBody);
oldExchange.getIn().setBody(tempAggFilePath.toFile());
return oldExchange;
}
} catch (IOException e) {
e.printStackTrace();
}
return oldExchange;
}
private void cleanDirectory(Path tempFileDir) {
for (File tempFile: tempFileDir.toFile().listFiles()){
if (!tempFile.isDirectory()){
tempFile.delete();
}
}
}
}
编辑:使用VisualVM并监视应用程序的运行情况,似乎Netty在发生类似Broken Pipe异常的情况时会开始产生额外的线程,但这些线程永远不会被清除。在我的Java程序运行17个小时后查看我的堆转储时,我发现最大的违规者(类的实例数)是io.netty.util.Recycler$DefaultHandle
和io.netty.channel.ChannelOutboundBuffer$Entry
的20.2%(59,630)和我的堆中的类分别为19.8%(58,306)。
关于Camel如何减轻这些设置的任何想法?
答案 0 :(得分:2)
Java会根据需要声明尽可能多的内存,直到配置的限制。
即使GC清除了几乎已满的对象,它通常也不会返回它声称给操作系统的内存。它会为未来的对象保留malloc()
d的块。
因此,您可能会期望几乎所有创建大量新对象的Java程序(即使它们都是短暂的)继续声明内存,直到其堆达到-Xmx
指定的大小。
Hotspot会自己进行内存管理 - 即malloc()
个大块,并按照自己的意愿使用它们,而不是每次创建对象时都执行malloc()
。
因此,free
不是查看Java程序是否正常运行的好工具。
要查看JVM内存,请使用VisualVM等工具 - 然后您可以查看堆大小,对象数等。如果您的程序确实泄漏了内存,您将在此处看到它。
如果您希望Java程序使用较少的内存,请将-Xmx
设置为较低,这将迫使GC在较小的内存分配中工作。