我在下面的代码中做错了吗?还是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似乎开始产生额外的线程,但这些线程永远都不会被清除。在我的Java程序运行17小时后查看堆转储时,我发现最大的冒犯者(该类的实例数)分别是io.netty.util.Recycler$DefaultHandleio.netty.channel.ChannelOutboundBuffer$Entry,分别为20.2%(59,630)和19.8%(58,306) )分别放在我的堆中。

关于骆驼如何减轻这些设置的任何想法?

最佳答案

Java将根据需要分配尽可能多的内存,直至达到配置的限制。

即使GC清除了对象(只有在几乎满时才执行操作),它通常也不会将已占用的内存返回给OS。它将保留其malloc() d的块以用于将来的对象。

因此,您会期望几乎所有正在创建许多新对象(即使它们是短暂的)的Java程序都可以继续声明内存,直到其堆达到-Xmx指定的大小为止。

Hotspot会执行自己的内存管理-即,它malloc()是大块,并按需要使用它们,而不是每次创建对象时都执行malloc()

因此,free并不是查看Java程序自身行为的好工具。

要查看JVM内存内部,请使用VisualVM之类的工具-然后您可以查看堆的大小,对象数等。如果程序确实在泄漏内存,则将在此处看到它。

如果希望Java程序使用较少的内存,请将-Xmx设置为较低,这将强制GC在较小的内存分配中工作。

10-05 22:37
查看更多