matoos32 / jfreechart-builder

A builder pattern module for working with the jfreechart library.
Other
11 stars 2 forks source link

Java 8 gapless time series chart OutOfMemoryError with XYShapeBuilder #71

Open matoos32 opened 1 year ago

matoos32 commented 1 year ago

Issue

I get an OutOfMemoryError with XYShapeAnnotation and Arrays.copyOf() when running the demo app's gapless-time annotations case with Java 8 on Ubuntu 20.04.5 LTS.

https://github.com/matoos32/jfreechart-builder/blob/48fdcab84d89e62c9815eb1a5f88b3ac3831ed98/demo-app/src/main/java/com/jfcbuilder/demo/JFreeChartBuilderDemo.java#L602

This happens if it's compiled with JDK 8 or 11 targetting 8 AND launching it with Java 8. If I launch it with Java 11 the issue doesn't occur. If I comment out the demo app's use of XYShapeBuilder it ceases to occur.

Java 8 Version

$ java -version
openjdk version "1.8.0_352"
OpenJDK Runtime Environment (build 1.8.0_352-8u352-ga-1~20.04-b08)
OpenJDK 64-Bit Server VM (build 25.352-b08, mixed mode)

Steps to Reproduce

  1. sudo apt-get install openjdk-8-jdk
  2. sudo apt-get install openjdk-11-jdk
  3. sudo update-alternatives --config java (select JDK 8)
  4. java -version (confirm Java 8)
  5. cd path/to/jfreechart-builder/repo
  6. mvn clean install
  7. cd path/to/.m2/repository/com/jfcbuilder/jfreechart-builder-demo/VERSION
  8. java -jar jfreechart-builder-demo-VERSION.jar
  9. From the Demonstrations drop-down pick the Gapless | Annotations and Markers one.
  10. Observe the app freezes then shows an eventual out of memory exception.
  11. Use the alternatives to switch to Java 11
  12. Launch the JAR again and pick the same annotations demo.
  13. Observe the app doesn't freeze and doesn't throw the exception.

I didn't test this on Windows.

Java 8 Stack trace

$ java -jar jfreechart-builder-demo-1.5.6.jar 
Exception in thread "AWT-EventQueue-1" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:3356)
    at sun.java2d.pisces.Helpers.widenArray(Helpers.java:160)
    at sun.java2d.pisces.Renderer.addLine(Renderer.java:295)
    at sun.java2d.pisces.Renderer.lineTo(Renderer.java:382)
    at sun.java2d.pisces.Stroker$PolyStack.pop(Stroker.java:1202)
    at sun.java2d.pisces.Stroker.emitReverse(Stroker.java:423)
    at sun.java2d.pisces.Stroker.finish(Stroker.java:446)
    at sun.java2d.pisces.Stroker.moveTo(Stroker.java:356)
    at sun.java2d.pisces.Dasher.goTo(Dasher.java:155)
    at sun.java2d.pisces.Dasher.somethingTo(Dasher.java:244)
    at sun.java2d.pisces.Dasher.curveTo(Dasher.java:540)
    at sun.java2d.pipe.RenderingEngine.feedConsumer(RenderingEngine.java:373)
    at sun.java2d.pisces.PiscesRenderingEngine.pathTo(PiscesRenderingEngine.java:484)
    at sun.java2d.pisces.PiscesRenderingEngine.strokeTo(PiscesRenderingEngine.java:363)
    at sun.java2d.pisces.PiscesRenderingEngine.strokeTo(PiscesRenderingEngine.java:163)
    at sun.java2d.pisces.PiscesRenderingEngine.getAATileGenerator(PiscesRenderingEngine.java:562)
    at sun.java2d.pipe.AAShapePipe.renderPath(AAShapePipe.java:147)
    at sun.java2d.pipe.AAShapePipe.draw(AAShapePipe.java:78)
    at sun.java2d.pipe.PixelToParallelogramConverter.draw(PixelToParallelogramConverter.java:148)
    at sun.java2d.SunGraphics2D.draw(SunGraphics2D.java:2502)
    at org.jfree.chart.annotations.XYShapeAnnotation.draw(XYShapeAnnotation.java:194)
    at org.jfree.chart.plot.XYPlot.drawAnnotations(XYPlot.java:3673)
    at org.jfree.chart.plot.XYPlot.draw(XYPlot.java:3048)
    at org.jfree.chart.plot.CombinedDomainXYPlot.draw(CombinedDomainXYPlot.java:447)
    at org.jfree.chart.JFreeChart.draw(JFreeChart.java:1160)
    at org.jfree.chart.ChartPanel.paintComponent(ChartPanel.java:1452)
    at javax.swing.JComponent.paint(JComponent.java:1056)
    at javax.swing.JComponent.paintChildren(JComponent.java:889)
    at javax.swing.JComponent.paint(JComponent.java:1065)
    at javax.swing.JComponent.paintChildren(JComponent.java:889)
    at javax.swing.JComponent.paint(JComponent.java:1065)
    at javax.swing.JLayeredPane.paint(JLayeredPane.java:586)
matoos32 commented 1 year ago

Some hypotheses on why it's happening:

  1. XYShapeBuilder doesn't map x-coordinates to time array indices, possibly leaving shape coordinates out of view, then with Java 8 that creates a problem?

  2. Based on the exception stack trace there's a logic issue in the JDK 8 implementation where an array being copied is huge, or there is an issue with indexing?

matoos32 commented 1 year ago

The workarounds are:

  1. If running with Java 8 don't use XYShapeBuilder
  2. Use a later version of Java.