如何解决 Out Of Memory 的问题

背景

在用 Excel Importer 导入数据,当数据量超过 1w 行,经常会出现 OutOfMemory 的错误。(用 Excel Exporter 导出数据时,也会有类似问题)。

通常的表现症状如下,即在导入成功若干行之后,爆出 java.lang.OutOfMemoryError: Java heap space 错误。

以下为某客户发生此类错误的具体信息:

  1. 日志问题如下( 只截取关键部分):

    image.png

  2. 该问题只发生在客户的测试环境,在本机开发环境不出现。
  3. 测试环境部署在私有云里,用 docker 部署的。
  4. 当导入数据量为 2W 行以上时,该错误总是发生在导入成功 1.2w 左右的时刻出现。如果数据量少于 1w 行,此问题不出现。

本文,我们来看看如何解决 java.lang.OutOfMemoryError: Java heap space 错误。

什么是 Java Heap Size ?

ava Heap Size是Java虚拟机(JVM)的一项配置参数,用于指定JVM运行时的堆内存大小。堆内存是Java程序中用于存储对象实例和数组的一块内存区域。在Java程序执行过程中,所有动态创建的对象都存储在堆内存中。

Java Heap Size对于Java程序的性能和稳定性非常重要。如果堆内存太小,可能导致内存不足错误(Out of Memory Error),因为JVM无法满足程序运行所需的内存。另一方面,如果堆内存太大,会导致垃圾回收频繁运行,影响程序的响应时间和性能。

通过调整Java Heap Size,可以根据应用程序的需求来优化内存的使用。通常,较大的堆内存对于内存密集型的应用程序(如大规模数据处理)是有益的,而对于较小的应用程序(如轻量级服务)可能需要较小的堆内存。

在Java中,可以通过以下参数来设置Java Heap Size:

  1. Xms: 指定JVM的初始堆内存大小。
  2. Xmx: 指定JVM的最大堆内存大小。

示例:

java -Xms256m -Xmx1024m MyApp 

上述命令将启动名为"MyApp"的Java应用程序,并将初始堆内存大小设置为256MB,最大堆内存大小设置为1024MB。

通常有哪些原因导致这个错误呢?

通常有两种原因:

  1. Java Heap Size 确实设置的太小了。
  2. 应用发生内存泄漏,导致在短时间内产生大量无法被 GC 的对象。

在本场景中,我们先排查第一个原因,因为:

  1. 首先,本问题发生在 Excel Importer 内部,本模块被下载过上万次,且由官方维护这么多年,出现问题的概率很小。我不是说官方组件一定不会出问题,但是从概率排查的角度,我们首先检验更脆弱的部份,而不是更稳固的部份,这是缺少其他信息时的最优选择。
  2. 其次,本问题发生在 Excel Importer 内部,我们没有源代码。就算确认了是 Excel Importer 的问题,我们的第一选择也应该是从外部规避它,只有这样才能保证项目按时完工。

验证问题

为了验证原因是否真的 Java Heap Size 设置的过小,我做了两个尝试:

  1. 降低本地开发环境 Mendix Studio Pro 中 Java Heap Size 的大小,看是否能重现该错误。设置方式如下:
  2. 在本地开发环境,导入一个 10w 行的 excel 文件,看是否能重现该错误。

经过上述实验,我发现,当我把 Java Heap Size 设置为 512MB 时,导入数据到 2w 条时,会爆出同样的错误。

这个现象证明,确实是 Java Heap Size 太小导致。

如何修复问题?

修复思路很简单,就是更改 Java Heap Size。

在 docker-mendix-buildpack 的文档中,我们知道,通过下属方式可以更改容器的环境变量:

我们在打开上图中的链接,进入 cf-mendix-buildpack 的文档(docker-mendix-buildpack 在运行时会调用 cf-mendix-buildpack),我们知道可以如下方式来设置 Java Heap Size:

于是,我们用以下命令来启动容器:

docker run -it \

-e HEAP_SIZE=4096M

...

但是,我们发现仅设置 HEAP_SIZE 这个环境变量后,问题依然没有解决。

后来,阅读 cf-mendix-buildpack 的代码,我发现:

  1. 如果未设置 HEAP_SIZE,则是可以根据 limit 被自动设置。
  2. 且如果 HEAP_SIZE 超过 limit,是不行的。

于是,我找到设置 limit 的方法,即设置 MEMORY_LIMIT 这个环境变量。

于是,我们用以下命令来启动容器:

docker run -it \ 
-e MEMORY_LIMIT=4096M 
... 

最终成功解决问题!

总结

  1. 分析问题原因时,首先检验更脆弱的部份,而不是更稳固的部份,这是缺少其他信息时的最优选择。
  2. 做少量的实验,可以更加笃定问题的根本原因,而不是靠盲目猜测。
  3. 开源工具,多看源码,你会有不一样的收获。

关于Mendix公司

在一个数字化先行的世界中,客户希望自己的每一项需求都得到满足,员工希望使用更好的工具来完成工作,而企业意识到自己只有通过全面数字化转型才能生存并取得成功。Mendix公司,a Siemens business正在迅速成为企业数字化转型的推动者。其业内领先的低代码平台和全方位的生态系统整合最先进的技术,帮助企业创造出提高互动性、简化操作和克服IT瓶颈的解决方案。Mendix公司以抽象化、自动化、云和协作为四大支柱,大幅提升开发者的生产力,并且依靠自己的工程协作能力和直观的可视化界面,帮助大量不熟悉技术的“公民”开发者在他们所擅长的领域创建应用程序。Mendix公司是权威行业分析师眼中的领导者和远见者,也是一个云原生、开放、可扩展、敏捷和饱经考验的平台。从人工智能和增强现实,到智能自动化和原生移动,Mendix公司已成为数字化先行企业的骨干。Mendix公司企业低代码平台已被全球4000多家领先的公司采用。

猜你喜欢

转载自blog.csdn.net/Mendix/article/details/132596134