4

我有一个 cfc 方法,它遍历一个列表并通过 cfhttp 进行一系列 SOAP 调用。然后将结果插入数据库。

该过程本身运行良好,问题是java内存慢慢填满,最终(取决于返回的记录中的元素数量)停止工作。没有错误或任何可见的东西它只是停止。如果我通过 Coldfusion 管理员查看应用程序日志文件,我会看到以下一个或两个错误:

GC overhead limit exceeded The specific sequence of files included or processed is:

或者

Java heap space The specific sequence of files included or processed is:

下面是我正在运行的代码的简化版本:

<cfsetting requesttimeout="3600">
<cfloop condition="thisPass lt 10">
    <cfscript>
        runtime = CreateObject("java","java.lang.Runtime").getRuntime();
        objSystem = CreateObject( "java", "java.lang.System" );
        soapBody = '';
        soapResponse = '';
        thisStruct = '';
        lock scope='application' type='exclusive' timeout='60' {


//This is where I am trying to manage the memory and call garbage collection


        try {
            freeMemory = runtime.freeMemory()/1024/1024;
            writeOutput("- fm = "&freeMemory);
            if (freeMemory < 200){
                objSystem.gc();
                sleep(1000);
                writeDump(' - dumping freeMemory');
             }
         }
         catch(any error) {
            writeDump(' - trying to dump GC as '&now()& ' freeMemory = '&freeMemory);
         }
         }
        </cfscript>
        <cfsavecontent variable="soapBody">
            <?xml version="1.0" encoding="utf-8"?>
            [ BUILD SOAP ENVELOP ]
        </cfsavecontent>

        <cfhttp url="[URL]" method="post" result="httpResponse" 
                        timeout="600" resolveurl="false">
                <cfhttpparam type="header" name="SOAPAction" value="[URL2]" />
                <cfhttpparam type="xml" value="#trim( soapBody )#"/>
            </cfhttp>


            <cfscript>
                soapBody = "";
                soapResponse = httpResponse.fileContent;
                soapResponse = xmlParse( soapResponse );
                thisStruct = xmlSearch(soapResponse,'/soap:Envelope/soap:Body/')[1].xmlChildren[1].xmlChildren[1].xmlChildren;
                writeOutput("-"&arrayLen(thisStruct)&' records');
                getPageContext().getOut().flush();
                if(arrayLen(thisStruct) == 2500){
                    thisPass = thisPass+1;
                } else {
                    writeOutput("- total records = "&(2500*(thisPass-1))+arrayLen(thisStruct));
                    thisPass = 100; // since looping while thisPass lt 10 this should prevent the next iteration
                }
            </cfscript>

            <cfloop from="1" to="#arrayLen(thisStruct)#" index="i">
                [RUN PROC TO INSERT RECORDS]
            </cfloop>
        </cfloop>

GC 似乎有时会释放一些内存,但没有任何可靠性。我知道 GC() 只是建议java 释放一些未使用的内存,但我不确定如何让它强制它释放内存。有可能某处有泄漏,但我没有看到。我希望这是我已经看过的显而易见的事情,并且我承认我的 Java 知识非常有限。

有没有一些java大师可以看到我的错误?

更新:这是一个输出示例,这有助于查看内存的下降。

有 236 个列表要循环

  1. 88185 - fm = 293.564407349 -6 记录 - 总记录 = 6
  2. 88389 - fm = 290.86995697 -116 条记录 - 总记录 = 116
  3. 88390 - fm = 308.382568359 -262 条记录 - 总记录 = 262
  4. 88839 - fm = 292.707099915 -2032 条记录- 总记录 = 2032
  5. 91088 - fm = 290.711753845 -6 记录 - 总记录 = 6
  6. 92998 - fm = 287.754066467 -5 条记录 - 总记录 = 5
  7. 95510 - fm = 309.919425964 -91 条记录 - 总记录 = 91
  8. 96478 - fm = 292.035064697 -1180 条记录- 总记录 = 1180
  9. 96479 - fm = 259.001213074 -1113 条记录 - 总记录 = 1113
  10. 96480 - fm = 261.121406555 -110 条记录 - 总记录 = 110
  11. 96796 - fm = 267.235244751 -2 记录 - 总记录 = 2
  12. 96799 - fm = 265.037582397 -0 记录 - 总记录 = 0
  13. 97435 - fm = 263.589103699 -2500 条记录 - fm = 227.629760742 -2500 条记录 - fm = 200.85987854 -2500 条记录 - fm = 202.156776428 -2500 条记录 - fm = 166.366210938 - 倾倒 freeMemory = 10966 条记录 - 共 106 条记录
  14. 98173 - fm = 160.579734802 - 转储 freeMemory -35 记录 - 总记录 = 35
  15. 99111 - fm = 176.218482971 - 转储 freeMemory -0 记录 - 总记录 = 0
  16. 100998 - fm = 194.708694458 - 转储 freeMemory -185 记录 - 总记录 = 185
  17. 101811 - fm = 160.61415863 - 转储 freeMemory -2500 条记录 - fm = 112.862670898 - 转储 freeMemory -2500 条记录 - fm = 86.2071380615 - 转储 freeMemory -2500 条记录 - fm = 52.9639358521 - 转储 freeMemory -2500 条记录 = 总共 85640 条记录
  18. 105014 - fm = 56.1721343994 - 转储 freeMemory -14 记录 - 总记录 = 14
  19. 105992 - fm = 73.0022964478 - 转储 freeMemory -14 记录 - 总记录 = 14
  20. 107539 - fm = 75.9522399902 - 转储 freeMemory -93 记录 - 总记录 = 93
  21. 107580 - fm = 58.345199585 - 转储 freeMemory -2500 条记录
4

4 回答 4

1

这不是 Java 不能很好地管理 GC 的情况,而是你没有管理你正在放入(和取出〜)内存的内容,以便垃圾收集器可以清理它们. 你(试图〜)治疗症状而不是这里的问题。

查看内存中的内容以及为什么您希望被 GC 处理的东西没有被 GC 处理。可能是您引用了共享范围内的东西(出乎意料),或者类似的东西。不过,在 Java 之上使用 ColdFusion 对这类事情进行故障排除是一门黑暗的艺术。

但是不要试图“修复”当你强制 GC 时没有被 GC 处理的东西的“问题”,修复导致你的内存 a) 填满的问题;b) 当你认为它应该是不具备 GC 能力的时候。

于 2013-02-08T17:16:13.423 回答
1

我可以说 force GC 方法在我尝试时对我没有多大帮助。一般来说,这里列出了首先要尝试的事情:

  • 确保在您调用变量的对象中使用 var 或 local
  • 如果您可以将处理移至数据库,请执行此操作
  • 删除<cfdump><cfdump>很重。如果您确实需要使用它,请仅关注需要的变量部分。也使用文本格式
  • 更改 JVM 以使用更多内存
  • 使用 Java 1.7 和 G1GC 。(请谨慎执行此操作,因为它可能尚不受支持)
  • 使用更少的查询

这是我会为您的上述代码考虑的列表

  • 我会将外观中的所有内容移动到一个函数和 var scope soapbody soapResponse, thisStruct
  • 在我完成这个Stuct之后,我会StructClear(thisStruct);
  • 写下writedump();他们喜欢的领域<cfdump>
于 2013-02-08T17:20:25.107 回答
1

经过大量搜索后,我发现这个特定问题的最佳“修复”是删除试图进行垃圾收集并增加 Java 堆大小的代码。在 /jrun/bin/jvm.config 文件中。

通过将参数更改为 VM 为:

java.args=-server -Xms2048m -Xmx2048m -Xmn1024m -Dsun.io.useCanonCaches=false -XX:MaxPermSize=192m -XX:+UseParallelGC -Xbatch -Dcoldfusion.rootDir={application.home}/ -Djava.security.policy={application.home}/servers/cfusion/cfusion-ear/cfusion-war/WEB-INF/cfusion/lib/coldfusion.policy -Djava.security.auth.policy={application.home}/servers/cfusion/cfusion-ear/cfusion-war/WEB-INF/cfusion/lib/neo_jaas.policy

我能够将初始堆大小 (Xms) 和最大堆大小 (Xmx) 增加到 2048m,并将“年轻一代”堆大小 (Xmn) 增加到 1024m 因为有人建议我年轻一代应该更小比初始和最大值更好的垃圾收集。

正如James所建议的那样,我注释掉了实际的进程(它们在一个函数中并且是 var'd),然后每次都取消注释它们一次运行所有的东西。我从中学到的是,大型 SOAP 响应是填满内存的,而不是我担心的泄漏。

正如Adam所提到的,这与 Java 管理 GC 无关,而是没有足够的空间来管理,因为我正在扔(出于某种原因,CF 不喜欢很好地处理 2500 条记录 SOAP 响应)去数字。

Adam说在 CF 中对 Java 内存进行故障排除是一门“黑艺术”也是正确的。通过使用服务器监视器http://localhost/CFIDE/administrator/monitor/launch-monitor.cfm并转到 Memory Usage -> Memory Usage Summary 下的统计选项卡,我可以看到内存慢慢填满并自行转储,即使在重新启动后没有任何进程在运行。永远无法弄清楚原因,但我可以看到正在运行的级别以及我正在达到顶峰。

jvm.config 文件中分配的默认内存是 512m,根本不足以处理正在发生的事情。可能有更好的方法来处理这个整个过程。可能必须实施亨利的建议并将其放入数据库并对其进行分块,尽管这对我来说听起来很笨拙。

我敢肯定,除了 CF 现在占用了大量资源之外,这个解决方案可能还有其他问题,但(目前)它似乎正在按需要工作。

更新: 我更改了 cfc 函数,因此我没有将所有内容都插入数据库,而是将所有 XML 写入文件,然后读取文件并插入数据库。不知何故,写入文件允许java“呼吸”足够长的时间来执行 GC() 本身。

于 2013-02-08T22:18:42.500 回答
0

AFAIK CF 不擅长运行长请求。据我所知,在请求完成之前不会释放内存。

我们尝试的是将一个长请求分解为 CF 可以管理的较小请求,并且通常在请求完成后会释放内存。

我们使用的一个遗留系统会将任务插入到数据库表中,而 CF 调度程序将一次处理一批。由于延迟,我讨厌这样做,但这是 CF7 天需要做的事情,而且从那以后它似乎没有改善。

于 2013-02-08T19:07:44.380 回答