【问题标题】:How to avoid memory leaks in Mule Applications?如何避免 Mule 应用程序中的内存泄漏?
【发布时间】:2018-08-29 03:41:47
【问题描述】:

为了避免Mule Applications中的内存泄漏,是否有一些特殊的事情必须考虑?

我们如何避免 Mule 应用程序中的内存泄漏?

例如;我们真的必须删除流变量吗? Mule 应用程序的开发人员必须明确做什么,Mule RuntimeJVM GC (自动)做什么?

【问题讨论】:

    标签: memory-leaks mule anypoint-studio mule-esb


    【解决方案1】:

    一般建议

    • 会话变量

    对于具有许多端点的应用程序,相比许多或 大的。每次消息通过时,会话范围都会被序列化和反序列化 一个端点,甚至是一个 VM 端点。所以如果一个应用程序有很多端点,它会涉及很多 序列化/反序列化。使用越来越少的会话变量有助于最大限度地减少这种情况 开销。

    • 有效载荷格式

    在性能方面,并非所有格式都相同。一些有效载荷格式允许更快 比其他人访问数据。对于 Mule 应用程序,Bean 有效负载往往是最快的。所以如果是 考虑到其他考虑,一个可行的选择是在 Java 对象中创建有效负载。

    • 数据提取

    Mule 表达式语言 (MEL) 可用于从消息中提取数据。8 在 性能,允许 MEL 提取数据可能比使用脚本语言更可取。 脚本语言是动态类型的。有些甚至在运行时被解释。那些因素 会产生可能会降低性能的开销。

    • 流参考

    流引用是在应用程序中启用流通信的一种非常直接的方式。 流引用比 VM 端点更适合流之间的通信。流动 引用将消息注入目标流而无需中间步骤。虽然 VM 连接器是一种内存协议。它模拟序列化和 反序列化部分消息。这种现象在 Session 范围内尤为显着。作为 因此,对于流间通信,流引用优于 VM 端点 因为前者避免了序列化和反序列化产生的不必要的开销。

    可以在 wrapper.conf 中为 Mule 设置 JVM 和 GC 标志。

    很容易对特定的 Java 虚拟机 (JVM) 或垃圾收集产生热情 (GC) 方法。 JRockit 与 HotSpot,并行标记和扫描 (MS) 与 G1。

    • MuleSoft 使用标准的 Oracle JVM HotSpot。热点是 支持良好且易于定制以用于各种目的。 MuleSoft 的性能测试强调吞吐量,因此 并行GC。 HotSpot 也很容易优化响应时间。这 以下部分中的提示显示了如何校准 HotSpot 吞吐量或响应时间。
    • 将初始堆大小和最大堆大小指定为相同的值。 这可以通过设置 MaxMetaspaceSize=MetaspaceSize 和 最大新尺寸=新尺寸。这样做可以避免 JVM 需要 在运行时动态分配额外的内存。标志是 在 wrapper.conf 中设置。

      例如 wrapper.java.additional.16=-XX:NewSize=1365m wrapper.java.additional.17=-XX:MaxNewSize=1365m wrapper.java.additional.18=-XX:MetaspaceSize=256m wrapper.java.additional.19=-XX:MaxMetaspaceSize=256m wrapper.java.additional.20=-Xms=2048m wrapper.java.additional.21=-Xmx=2048m

    • 这种动态重新分配至少有两个原因 阻碍性能。首先,JVM对每个堆执行一次major GC 调整大小。 Full GC 会停止所有线程一段时间。成立 即使使用并发标记和清除 (CMS)。世界级的 应始终最小化,其他条件相同。这是 对于优先考虑低响应时间的应用程序尤其重要。 当内存紧张时,动态堆大小调整会产生第二个问题。 假设 JVM 在运行时增加了它的堆大小,并且系统 没有足够的可用内存页。作为一个 结果,内核选择的进程的某些页面可能会被换出 到磁盘。由于磁盘增加,这种情况会导致减速 IO。

    垃圾收集 HotSpot 配备了三种规范的垃圾回收 (GC) 机制。这些是 串行、并行和并发标记和清除 (CMS)。18 垃圾优先 (G1) 最近被 添加到列表中。 19 JVM 默认在具有 2 个或更多物理的机器上使用并行 GC 处理器和 2 GB 或更多 GB 的物理内存。

    并行 GC 是 HotSpot JVM 中默认的垃圾收集算法。触发时,它使用 多个线程扫描、移动和收集堆中不可达的对象。 CMS GC(并发标记扫描) 并发标记和清除 (CMS) GC 旨在通过运行来减少应用程序暂停 大多数清理阶段与应用程序线程同时进行,因此它提供了更多 控制影响应用程序响应时间的停顿时间。 这是一个示例,演示如何将 JVM 设置为使用 CMS,以及其他选项。设置 在 Mule 的 wrapper.conf 文件中。第 6 节,“示例配置文件”提供了额外的 设置标志的上下文。

    wrapper.java.additional.22=-XX:+UseConcMarkSweepGC
    wrapper.java.additional.23=-XX:CMSInitiatingOccupancyFraction=65
    wrapper.java.additional.24=-XX:UseCMSInitiatingOccupancyOnly
    

    标志 -XX:CMSInitiatingOccupancyFraction 指定总堆的百分比 用法。当达到该百分比时,JVM 将触发 CMS GC。值 40 到 70 通常足以满足在 Mule 上运行的应用程序。如果值太低,可能会导致 过度的、过早的收集。通常建议从相对较高的值开始 -XX:CMSInitiatingOccupancyFraction 并根据需要减少它以优化最少的 CMS 最佳表现的活动。 在指定 -XX 时指定 -XX:+UseCMSInitiatingOccupancyOnly: +CMSInitiatingOccupancyFraction 。否则,JVM 会尝试动态调整该值 对于 -XX:+CMSInitiatingOccupancyFraction。在大多数生产中不希望改变值 情景。那是因为动态调整是基于统计分析,可能不 可靠地考虑负载峰值。

    GC 日志记录是性能测试的好主意。 GC 日志一旦启用,将提供 关于堆中的活动以及它们如何影响运行时的非常有价值的信息 表现。 GC 日志对磁盘 IO 的开销往往很小。 这是一个如何启用 GC 日志记录各个方面的示例。将这些配置添加到 Mule 的 wrapper.conf 文件。

    wrapper.java.additional.4=-XX:+PrintGCApplicationStoppedTime
    wrapper.java.additional.5=-XX:+PrintGCDetails
    wrapper.java.additional.6=-XX:+PrintGCDateStamps
    wrapper.java.additional.7=-XX:+PrintTenuringDistribution
    wrapper.java.additional.8=-XX:ErrorFile=%MULE_HOME%/logs/err.log
    wrapper.java.additional.9=-Xloggc:%MULE_HOME%/logs/gc.log
    wrapper.java.additional.10=-XX:+HeapDumpOnOutOfMemoryError
    

    【讨论】:

    • 非常感谢您对性能调优、GC 的不同配置设置和其他 GC 相关主题的详细回答,但这个答案并不是我原来问题的真正答案...
    【解决方案2】:

    找到内存泄漏嫌疑人的一个好方法是在您开始看到内存回收后主要 GC 下降后立即进行堆转储(所有节点)。有多种工具可以帮助分析内存泄漏。

    主题中有a great blog post。这总结了一些与内存泄漏相关的问题,例如以下发现:

    发现池化内存管理器通常会占用 10% 的 JVM 堆并与之共存而不释放。 修复:切换 Grizzly 内存管理器实现 HeapMemoryManager。请注意,HeapMemoryManager 是默认实现,Grizzly 建议使用它来提高性能;尽管如此,Mule 将 PoolMemoryManager 实现视为默认实现。

    Wrapper.conf 更改:

    wrapper.java.additional.<XX>=-Dorg.glassfish.grizzly.DEFAULT_MEMORY_MANAGER=org.glassfish.grizzly.memory.HeapMemoryManager
    

    发现异步日志记录被广泛使用,并且观察到相关的 Log4J 占用了大量 JVM 内存。 256*1024 插槽的默认设置显然也是如此高的。由于此 RingBuffer 不会增长或缩小,因此将每个插槽分配为单独的对象 (RingBufferLogEvent) 的高固定大小可能会占用大量内存。

    修复将 wrapper.conf 或 log4j2.xml 中的 Log4J RingBuffer 大小减少到 128

    wrapper.java.additional.<XX>=-DAsyncLoggerConfig.RingBufferSize=128
    

    或者,在 log4j2.xml 中:

    <AsyncLogger name="DebugLog" level="info" includeLocation="true" ringBufferSize="128">
    

    由于用于聚合器组件(拆分器-聚合器模式)的默认 HazelCast 实现导致内存泄漏。

    发现:堆分析指出在特定流中使用的拆分器-聚合器组件中使用的默认 HazelCast 对象存储实现会占用内存。商店似乎没有适当地过期。

    修复:已编写自定义对象存储实现(PartitionedInMemoryObjectStore 的子类)并明确定义了条目的 TTL (TimeToLive)。

    @Override 
    public void expire(int entryTTL, int maxEntries, String partitionName) throws ObjectStoreException
    {
        super.expire(entryTTL, maxEntries, partitionName);
        if (getPrivatePartitionSize(partitionName) == 0) {
    disposePartition(partitionName);
        }
    }
    

    参考:https://dzone.com/articles/enduring-black-fridays-with-mulesoft-apis

    【讨论】:

    • 非常感谢您提供指向这篇精彩博文的链接。这些绝对是重要的事情,但我仍然缺少指导方针或最佳实践来避免实际上由我们的流量引起的泄漏......但是是的。这类信息正是我要寻找的。​​span>
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-27
    相关资源
    最近更新 更多