【问题标题】:Apache Camel Netty4 high memory usageApache Camel Netty4 高内存使用
【发布时间】:2017-02-24 13:25:45
【问题描述】:

我在下面的代码中做错了吗?或者 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$DefaultHandleio.netty.channel.ChannelOutboundBuffer$Entry,分别为 20.2% (59,630) 和 19.8% (58,306) ) 我堆中的类。

关于 Camel 如何缓解这些设置的任何想法?

【问题讨论】:

  • "我的内存使用量每 30 秒增加 4MB。" -- 怎么衡量的?
  • 啊抱歉,我解释了其他所有内容,但...哈哈。除了带有 Java 进程的终端之外,我已经关闭了所有东西,我正在查看 Linux 中的 free -m 命令。在我运行该进程之前,内存处于稳定状态,所以我不知道其他任何东西都在同时耗尽内存。我应该更新问题
  • 磁盘上的 1.3MB 由于压缩等原因,在加载到内存时可能会导致 4MB。因此,每 30 秒,您会将传入的数据聚合到一个文件中,然后将其保存在内存中。内存增加的原因可能是处理完成时文件没有从内存中删除,或者 GC 只是在需要时才启动。如果您只是将传入的数据附加到您的交换主体而不是创建一个文件,会发生什么?
  • @noMad17 我们之前已将传入数据附加到交易所的正文中,但结果仍然相同。我什至通过System.gc()进入代码,看看我是否可以让垃圾收集器跳进去。它似乎对正在使用的内存没有影响。
  • 好的,.to( pushFile ) 中发生了什么?它实际上是对文件组件的调用,还是您在其中做了一些可能导致文件保存在内存中的事情?

标签: java apache-camel netty


【解决方案1】:

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

即使 GC 清除了 objects ,它只会在几乎满时才会这样做,它通常不会将它声称的内存返回给操作系统。它将保留它拥有malloc()d 的块以供将来的对象使用。

因此,您会期望几乎所有创建大量新对象(即使它们是短暂的)的 Java 程序都会继续申请内存,直到其堆达到-Xmx 指定的大小。

Hotspot 进行自己的内存管理——也就是说,它malloc()s 大块,并根据需要使用它们,而不是每次创建对象时都执行malloc()

因此,free 不是查看 Java 程序是否正常运行的好工具。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-04
    • 2018-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-30
    相关资源
    最近更新 更多