如何避免 Mule 应用程序中的内存泄漏?
How to avoid memory leaks in Mule Applications?
在 Mule Applications 中是否有一些必须考虑的特殊事项来避免内存泄漏?
我们如何避免 Mule 应用程序中的内存泄漏?
例如;我们真的必须删除流变量吗? Mule 应用程序的开发人员必须明确完成哪些工作以及 Mule Runtime and the JVM GC?
(自动)完成的工作
一般建议
- Session 变量
对于具有许多端点的应用程序,更喜欢更少和更小的 session 变量
large ones.The session scope 每次经过一个消息都会被序列化和反序列化
端点,甚至是 VM 端点。所以如果一个应用程序有很多端点,它会涉及很多
serializations/deserializations。使用更少和更小的 session 变量有助于最大限度地减少这种情况
开销。
- 有效载荷格式
就性能而言,并非所有格式都一样。一些有效负载格式允许更快
比其他人访问数据。 Bean 负载对于 Mule 应用程序来说往往是最快的。所以如果是
考虑到其他考虑因素的可行选项,在 Java objects.
中创建有效负载
- 数据提取
Mule 表达语言 (MEL) 可用于从消息中提取数据。8 就
性能,允许 MEL 提取数据比使用脚本语言更可取。
脚本语言是动态类型的。有些甚至在运行时被解释。那些因素
会产生可能降低性能的开销。
- 流程参考
流引用是在应用程序中启用流通信的一种令人愉快的直接方式。
流引用比 VM 端点更适合流之间的通信。流
引用将消息注入目标流而无需中间步骤。虽然
VM 连接器是一种 in-memory 协议。它模拟序列化和
反序列化部分消息。这种现象在 Session 范围内尤为显着。作为
因此,就 inter-flow 通信而言,流引用优于 VM 端点
因为前者避免了序列化和反序列化产生的不必要的开销。
可以在 wrapper.conf 中为 Mule 设置 JVM 和 GC 标志。
很容易对特定的 Java 虚拟机 (JVM) 或垃圾 collection 产生热情
(GC) 方法。 JRockit 与 HotSpot,并行 mark-and-sweep (MS) 与 G1.
- MuleSoft 使用标准的 Oracle JVM HotSpot。热点是
well-supported 并且易于针对各种用途进行自定义。
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 会停止所有线程一段时间。那持有
即使使用并发 mark-and-sweep (CMS)。 World-stopping
在其他条件相同的情况下,应始终将其最小化。这是
对于优先考虑低响应时间的应用程序尤其重要。
当内存紧张时,动态调整堆大小会产生第二个担忧。
假设 JVM 在运行时和系统期间增加了它的堆大小
没有足够的空闲内存页面随时可用。作为一个
结果,kernel-chosen 进程的一些页面可能被换出
到磁盘。由于磁盘增加,这种情况会导致减速
IO.
垃圾Collection
HotSpot 配备了三种规范垃圾 collection (GC) 机制。这些都是
串行、并行和并发 mark-and-sweep (CMS)。18 垃圾优先 (G1) 最近
添加到列表中。19 JVM 在具有 2 个或更多物理内存的机器上默认使用并行 GC
处理器和 2 GB 或更多 GB 的物理内存。
并行 GC 是 HotSpot JVM 中的默认垃圾 collection 算法。触发时,它使用
多个线程扫描、移动和收集堆中无法访问的objects。
CMS GC(Concurrent-Mark-Sweep)
并发 mark-and-sweep (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 上的应用程序 运行。如果值我太低,它可能会导致
过多的,过早的 collections。通常建议以相对较高的值开始
-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
找到内存泄漏嫌疑人的一个好方法是在您开始看到内存回收下降后立即进行(所有节点的)堆转储 post-major GC。有多种工具可以帮助分析内存泄漏。
题目中有a great blog post。这总结了一些 memory-leak 相关问题,例如以下发现:
发现:池化内存管理器通常会占用 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[=16=中将Log4J RingBuffer大小减少到128 ]
wrapper.java.additional.<XX>=-DAsyncLoggerConfig.RingBufferSize=128
或者,在log4j2.xml中:
<AsyncLogger name="DebugLog" level="info" includeLocation="true" ringBufferSize="128">
由于用于聚合器组件(splitter-Aggregator 模式)的默认 HazelCast 实现导致内存泄漏。
发现:堆分析指出内存在默认情况下被阻止,HazelCast objectstore 实现在特定流程中使用的 splitter-aggregator 组件中使用。似乎商店没有适当过期。
修复:编写了自定义对象存储实现(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
在 Mule Applications 中是否有一些必须考虑的特殊事项来避免内存泄漏?
我们如何避免 Mule 应用程序中的内存泄漏?
例如;我们真的必须删除流变量吗? Mule 应用程序的开发人员必须明确完成哪些工作以及 Mule Runtime and the JVM GC?
(自动)完成的工作一般建议
- Session 变量
对于具有许多端点的应用程序,更喜欢更少和更小的 session 变量 large ones.The session scope 每次经过一个消息都会被序列化和反序列化 端点,甚至是 VM 端点。所以如果一个应用程序有很多端点,它会涉及很多 serializations/deserializations。使用更少和更小的 session 变量有助于最大限度地减少这种情况 开销。
- 有效载荷格式
就性能而言,并非所有格式都一样。一些有效负载格式允许更快 比其他人访问数据。 Bean 负载对于 Mule 应用程序来说往往是最快的。所以如果是 考虑到其他考虑因素的可行选项,在 Java objects.
中创建有效负载- 数据提取
Mule 表达语言 (MEL) 可用于从消息中提取数据。8 就 性能,允许 MEL 提取数据比使用脚本语言更可取。 脚本语言是动态类型的。有些甚至在运行时被解释。那些因素 会产生可能降低性能的开销。
- 流程参考
流引用是在应用程序中启用流通信的一种令人愉快的直接方式。 流引用比 VM 端点更适合流之间的通信。流 引用将消息注入目标流而无需中间步骤。虽然 VM 连接器是一种 in-memory 协议。它模拟序列化和 反序列化部分消息。这种现象在 Session 范围内尤为显着。作为 因此,就 inter-flow 通信而言,流引用优于 VM 端点 因为前者避免了序列化和反序列化产生的不必要的开销。
可以在 wrapper.conf 中为 Mule 设置 JVM 和 GC 标志。
很容易对特定的 Java 虚拟机 (JVM) 或垃圾 collection 产生热情 (GC) 方法。 JRockit 与 HotSpot,并行 mark-and-sweep (MS) 与 G1.
- MuleSoft 使用标准的 Oracle JVM HotSpot。热点是 well-supported 并且易于针对各种用途进行自定义。 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 会停止所有线程一段时间。那持有 即使使用并发 mark-and-sweep (CMS)。 World-stopping 在其他条件相同的情况下,应始终将其最小化。这是 对于优先考虑低响应时间的应用程序尤其重要。 当内存紧张时,动态调整堆大小会产生第二个担忧。 假设 JVM 在运行时和系统期间增加了它的堆大小 没有足够的空闲内存页面随时可用。作为一个 结果,kernel-chosen 进程的一些页面可能被换出 到磁盘。由于磁盘增加,这种情况会导致减速 IO.
垃圾Collection HotSpot 配备了三种规范垃圾 collection (GC) 机制。这些都是 串行、并行和并发 mark-and-sweep (CMS)。18 垃圾优先 (G1) 最近 添加到列表中。19 JVM 在具有 2 个或更多物理内存的机器上默认使用并行 GC 处理器和 2 GB 或更多 GB 的物理内存。
并行 GC 是 HotSpot JVM 中的默认垃圾 collection 算法。触发时,它使用 多个线程扫描、移动和收集堆中无法访问的objects。 CMS GC(Concurrent-Mark-Sweep) 并发 mark-and-sweep (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 上的应用程序 运行。如果值我太低,它可能会导致 过多的,过早的 collections。通常建议以相对较高的值开始 -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
找到内存泄漏嫌疑人的一个好方法是在您开始看到内存回收下降后立即进行(所有节点的)堆转储 post-major GC。有多种工具可以帮助分析内存泄漏。
题目中有a great blog post。这总结了一些 memory-leak 相关问题,例如以下发现:
发现:池化内存管理器通常会占用 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[=16=中将Log4J RingBuffer大小减少到128 ]
wrapper.java.additional.<XX>=-DAsyncLoggerConfig.RingBufferSize=128
或者,在log4j2.xml中:
<AsyncLogger name="DebugLog" level="info" includeLocation="true" ringBufferSize="128">
由于用于聚合器组件(splitter-Aggregator 模式)的默认 HazelCast 实现导致内存泄漏。
发现:堆分析指出内存在默认情况下被阻止,HazelCast objectstore 实现在特定流程中使用的 splitter-aggregator 组件中使用。似乎商店没有适当过期。
修复:编写了自定义对象存储实现(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