我们有一个Coldfusion应用程序,该应用程序运行一个大型查询(最多10万行),然后以HTML形式显示。然后,UI提供一个“导出”按钮,该触发器使用cfspreadsheet标签和电子表格功能(尤其是用于建立行列值的电子表格SetCellValue),用于格式化的电子表格FormatRow和电子表格格式细胞功能触发将报告写入.xlsx格式的Excel电子表格中。然后使用以下命令将ssObj写入文件:
<cfheader name="Content-Disposition" value="attachment; filename=OES_#sel_rtype#_#Dateformat(now(),"MMM-DD-YYYY")#.xlsx">
<cfcontent type="application/vnd-ms.excel" variable="#ssObj#" reset="true">
其中ssObj是SS对象。我们看到的文件大小约为5-10 Mb。
但是...用于创建此报告和写入文件的内存使用量增加了大约1GB。复杂的问题是Java GC导出完成后不会立即释放内存。当我们有多个用户正在运行并导出这种类型的报告时,内存将继续攀升并达到分配的堆大小,并且会破坏serer的性能,直至导致服务器瘫痪。通常需要重新启动才能清除它。
这是正常/预期的行为还是我们应该如何处理此问题?导出完成后,是否可以根据需要轻松释放此操作的内存使用情况,以便其他运行报告的人员可以轻松访问释放的报告空间?这种5-10Mb文件的内存使用情况与cfspreadsheet函数并写出对象是否相同?
我们试图暂时删除昂贵的格式化功能,但是对于创建和写入.xlsx文件,内存使用仍然很大。我们还尝试使用电子表格AddRows方法和cfspreadsheet action =“ write” query =“ queryname”标记传入查询对象,但这也占用了大量内存。
为什么这些功能会导致内存浪费?没有内存不足问题的最佳生成Excel SS文件的方法是什么?
我应该添加服务器在Windows上的Apache / Tomcat容器中运行,并且我们正在使用CF2016。
答案 0 :(得分:5)
在上一份工作中,我们遇到了此类问题(CF和内存)。大文件上传消耗了内存,大excel导出消耗了内存,这将要发生。随着应用程序用户群的增长,您将遇到这些内存占用请求杀死其他用户的站点的地步。
从内存设置开始。通过将应用程序分配的内容增加一倍或三倍,您可能会全面受益。另外,请确保您使用的CF版本是受支持的JDK的最新版本。这也可以带来巨大的改变。
大文件上传会影响发出请求的实例的性能。这意味着在同一实例上执行正常请求的其他人不必要地等待这些资源。我们专用了一个实例池来仅处理文件上传。通过负载平衡器将特定的URL路由到这些实例,因此应用程序更加满意。
该应用程序还处理了海量数据,用户一直想要“所有数据”。我们必须强制搜索结果和某些数据集以减少屏幕上显示的数量。 DB对这个决定感到非常满意。数据导出已移到队列中,因此他们可以在正常页面请求之外制作那些较大的excel文件。也许他们立即获得了数据,也许等待了一段时间才收到通知。无论哪种方式,应用程序的整体性能都更好。
答案 1 :(得分:1)
对于OP来说可能有点晚了,但是自从我结束在这里之后,其他人也可能会迟到。尽管在这里的其他答案+评论中有很多与内存有关的常规声音建议,但我怀疑OP确实遇到了一个真正的内存泄漏错误,该错误已从CF11到CF11的CF电子表格功能中报告过到CF2018。
当生成电子表格对象并使用cfheader
+ cfcontent
进行服务而不将其写入磁盘时,即使进行了仔细的变量范围设置,也永远不会收集内存。因此,如果您的应用使用此方法运行了足够的Excel导出,则最终将使内存最大化,然后无限期地使CPU最大化,从而需要CF重新启动。
请参见https://tracker.adobe.com/#/view/CF-4199829-我不知道他是否在使用SO,但请给Trevor Cotton提交错误报告和解决方法:
因此,给定一个电子表格对象,它是使用spreadsheetNew()
在内存中创建的,并且从未写入磁盘,那么这会导致内存泄漏:
<cfheader name="Content-disposition" value="attachment;filename=#arguments.fileName#" />
<cfcontent type="application/vnd.ms-excel" variable = "#SpreadsheetReadBinary(arguments.theSheet)#" />
...但这不是:
<cfset local.tempFilePath = getTempDirectory()&CreateUUID()&arguments.filename />
<cfset spreadsheetWrite(arguments.theSheet, local.tempFilePath, "", true) />
<cfset local.theSheet = spreadsheetRead(local.tempFilePath) />
<cffile action="delete" file="#local.tempFilePath#" />
<cfheader name="Content-disposition" value="attachment;filename=#arguments.fileName#" />
<cfcontent type="application/vnd.ms-excel" variable = "#SpreadsheetReadBinary(local.theSheet)#" />
这不是必需的,但是Adobe似乎并不急于解决此问题,并且我已经证实它在CF2016中对我有效。