8

我们的 Web 应用程序遇到一个复杂的情况

它是由 STS/ 开发的 Spring 应用程序Tomcat 7。应用程序集成后Jasper report 4.6.0,总是抛出 `OutOfMemoryError: PermGen Space. 然后让它工作的唯一方法是重新启动应用程序。但过了一会儿又发生了。这是异常之前的日志:

Oct 17, 2012 3:42:27 PM org.apache.jasper.compiler.TldLocationsCache tldScanJar
INFO: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
Oct 17, 2012 3:42:30 PM org.apache.catalina.core.ApplicationDispatcher invoke
SEVERE: Servlet.service() for servlet jsp threw exception

这是异常中的一个部分,我在其中发现了一些内容Jasper

at org.apache.jasper.compiler.JDTCompiler.generateClass(JDTCompiler.java:442)
at org.apache.jasper.compiler.Compiler.compile(Compiler.java:378)
at org.apache.jasper.compiler.Compiler.compile(Compiler.java:353)
at org.apache.jasper.compiler.Compiler.compile(Compiler.java:340)
at org.apache.jasper.JspCompilationContext.compile(JspCompilationContext.java:646)
at org.apache.jasper.servlet.JspServletWrapper.loadTagFile(JspServletWrapper.java:240)
at org.apache.jasper.compiler.TagFileProcessor.loadTagFile(TagFileProcessor.java:578)
at org.apache.jasper.compiler.TagFileProcessor.access$000(TagFileProcessor.java:49)
at org.apache.jasper.compiler.TagFileProcessor$TagFileLoaderVisitor.visit(TagFileProcessor.java:655)

以下是出现这种情况时的一些发现:

  1. 该问题可能发生在没有任何 Jasper 报告组件的页面上。Jasper Report bean 似乎一直在尝试查找标记库,而a request is processed by the back end and responded to the front end. 通常从日志文件中我可以看到,在所有后端操作(JPA 管理)完成之前,上述异常不会抛出

  2. 在调试模式下运行 log4J 时,我看到大量信息显示诸如解析/渲染 Jasper 模板中的所有组件(文本字段、笔、框......),从巨大的日志中有一个小切口:

    2012-10-17 15:29:12,025 -- DEBUG -- org.apache.commons.digester.Digester.sax -- startElement(http://jasperreports.sourceforge.net/jasperreports,textElement,textElement)
    2012-10-17 15:29:12,025 -- DEBUG -- org.apache.commons.digester.Digester --   Pushing body text ''
    2012-10-17 15:29:12,025 -- DEBUG -- org.apache.commons.digester.Digester --   New match='jasperReport/summary/band/textField/textElement'
    2012-10-17 15:29:12,025 -- DEBUG -- org.apache.commons.digester.Digester --   Fire begin() for FactoryCreateRule[className=net.sf.jasperreports.engine.xml.JRTextElementFactory, attributeName=null, creationFactory=net.sf.jasperreports.engine.xml.JRTextElementFactory@12dc6007]
    2012-10-17 15:29:12,025 -- DEBUG -- org.apache.commons.digester.Digester -- [FactoryCreateRule]{jasperReport/summary/band/textField/textElement} New net.sf.jasperreports.engine.design.JRDesignTextField
    2012-10-17 15:29:12,025 -- DEBUG -- org.apache.commons.digester.Digester.sax -- ignorableWhitespace()
    

    尽管如此,当对不包含任何 Jasper 组件的页面的请求时,仍会生成此日志。

我做了一些研究,但仍然找不到解决这个问题的方法。

  1. 第一个问题是即使jasperreport bean在应用程序中也有一个问题,为什么当它甚至没有与当前服务自动装配时它总是在运行(意味着当前页面没有任何 jasper 组件)。这种情况有解决方案/答案吗?

  2. 同样来自异常消息 扫描了至少一个 JAR 以查找 TLD,但未包含 TLD。在 org.apache.jasper.compiler.JDTCompiler.generateClass(JDTCompiler.java:442)

    应该来自 Tomcat,并且Tomcat never contains any JSTL jar,然后我假设它找不到匹配的 TLD 来解析 jasper 报告,因此对所有 jar 进行全面扫描。如果是这样,那么为什么有大量的调试日志org.apache.commons.digester.Digester实际上似乎忙于解析 jasper 模板?

一般来说,做这个线程只是试图找出解决问题的方法,同时也找到为什么Jasper在一个不需要它的地方如此活跃的答案,以及我们如何让tomcat正确解析模板?

如果过于冗长,请道歉,并感谢您的任何提示。

4

5 回答 5

9

感谢大家提供有关此问题的解决方案,我已针对我的情况确定了该问题,以下是解决方案:

使用 .jasper 而不是 .jrxml 作为模板!

众所周知.jasper,这是一个已编译的模板以及.jrxml该模板的 ASCII 源代码,因此如果我们在当前的 spring 应用程序中使用原始源代码文件(jrxml)作为模板,那么至少 spring 框架必须编译源代码文件。这是留给 Spring 框架的效率问题,因为它是 jasper bean 来处理编译,并且不能保证编译只执行一次,并且只在应用程序启动时发生。

简而言之,将所有模板替换为 .jasper 文件后,日志大小已显着减小,并且不再出现内存不足的问题。我猜 Spring 容器在运行时可能会消耗大量资源来将 jrxml 编译为 jasper。所以它可能是 Jasper 或 Spring 应该改进的东西......

于 2013-03-19T16:35:55.970 回答
7

当 JVM 的 permgen 空间中有太多 .class 文件,由于引用了 AppClassLoader 之外的对象而无法进行垃圾回收时,就会发生异常。它通常指出您的应用程序存在一些内存泄漏。

这篇文章对 java.lang.OutOfMemoryError: PermGen space 错误有一个清晰的解释,下面的文章有关于如何修复它的建议。在 SO 上提出了一个类似(但不完全相同)的问题,让您知道是否错过了它。我希望它有所帮助。

正如 jakub 所提到的,设置-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled或设置更高的值XX:MaxPermSize可能对您有用。但从我所阅读的内容来看,这似乎不是一个永久的解决方案。(我不是这方面的大师:))。

于 2012-10-18T02:38:53.477 回答
2

尝试在您的 VM 中设置这些参数。这些应该能够让 GC 清理你的 permGen。

-XX:+UseConcMarkSweepGC
-XX:+CMSPermGenSweepingEnabled
-XX:+CMSClassUnloadingEnabled
于 2012-10-17T21:06:33.450 回答
2

我开发了一个使用 JasperReports 4.5.1 的 Web 应用程序

我使用 Tomcat 6.0.26 作为容器。(Win7, JDK 1.6.0_25)

关闭tomcat时,它会抛出:

Web 应用程序创建了一个 ThreadLocal,其键类型为 [net.sf.jasperreports.engine.util.JRFontUtil$1](值 [net.sf.jasperreports.engine.util.JRFontUtil$1@7892f1]),值类型为 [java .util.HashSet](值 [[]])但在 Web 应用程序停止时未能将其删除。这很可能造成内存泄漏。

请访问网站:

http://community.jaspersoft.com/questions/534340/memory-leak-jr-373

于 2012-11-01T10:00:33.823 回答
0

由于 PermGen 主要包含类元数据,以及常量和内部字符串,您可以从两个方向搜索:

  • 检查 webapp 是否包含(太多)无用的 jar,这些 jar 可能由于扫描而被加载

  • 查看您是否有大量使用堆转储的常量字符串(例如,大量大型 JSP),或者您的代码是否使用 String.intern()


实际上,您没有指定您使用的 Java 版本:对于 Java 7,字符串可能不是问题。

您可以做的是使用 VisualGC 插件使用 JVisualVM 观察应用程序,以查看生成的状态、加载的类的数量以及在 OOM 时是否出现激增,或者是否构建缓慢-向上。

于 2012-10-17T21:25:01.817 回答