JDK-8159860 : JavaFX Path drawing appears to leak native memory
  • Type: Bug
  • Component: javafx
  • Sub-Component: graphics
  • Affected Version: 8u92,9
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: os_x,windows_10
  • CPU: x86
  • Submitted: 2016-06-18
  • Updated: 2020-01-31
  • Resolved: 2016-06-23
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
8u112Fixed 9Fixed
Repeated drawing of Paths appears to slowly leak native memory.  A project is attached that can be run with gradle.  

The test code paints 100 paths repeatedly.  While it is running, observe the process memory usage with a tool like Process Explorer or Task Manager on Windows, or Activity Monitor on OS X.

To be sure there is a real native leak, limit the Java heap  and allow the program to run for 30 minutes or so, recording the processes totally memory usage every 5 minutes.  Memory fluctuations early on can make it difficult to see that the memory usage of the process steadily climbs.
Using a tool like jvisualvm you can see that Java heap is not leaking.

The leak is present with -Dprism.order=sw as well as using the GPU accelerated pipeline.

Using jvisualvm to trigger GC has no effect on the native memory usage. (My thinking is that a finalizer might be used to reclaim native resources.)
Changeset: fed535b1d3e6 Author: flar Date: 2016-06-23 15:16 -0700 URL: http://hg.openjdk.java.net/openjfx/9-dev/rt/rev/fed535b1d3e6 8159860: JavaFX Path drawing appears to leak native memory Reviewed-by: kcr ! modules/graphics/src/main/native-prism/Renderer.c

Approved to backport to 8u-dev (8u112)


The updated patch also applies cleanly to 8u-dev and fixes the problem there as well.

Here is an updated fix that deals with the different alpha maps used for AA and non-AA rendering: http://cr.openjdk.java.net/~flar/JDK-8159860/webrev.00/

Looking closer it seems that the handling of maxAlpha is awkward and inefficient. setMaxAlpha is only ever called with two possible values: 9 or 0. Rather than recomputing the table every time, it would make more sense to just flip between the two different tables. OpenPiscesPrismUtils seems to cache two different renderers which appears accomplish the same thing, but the native version is constantly re-initializing a render which is what makes this leak in the first place.

This should be simple and safe to back port to 8u. I would appreciate it if that could happen for the next available 8u release.

I confirmed that setMaxAlpha was being called repeatedly and leaking. Simple fix attached.

I traced a bit farther. ShapeUtil.rasterizeShape calls NativePiscesRasterizer.getMaskData, which will call init (NativePiscesRasterizer.init) whenever the 'antialiasedShape' parameter changes between calls. NativePiscesRasterizer.init is a native method that calls Renderer_setup, which calls setMaxAlpha, which allocates a new alphaMap. Since ShapeUtil can be configured to use wither NativePiscesRaterizer or OpenPiscesRasterizer, my next experiment was to force the 'Open' version... Well that was interesting. Setting "prism.nativepisces" to "false" gives me the java-based Pisces Rasterizer, which doesn't work. When I add that to my sample the lines don't draw at all. It also either doesn't leak, or it leaks much slower (on OS X).

I started looking around... I see an allocation of 'alphaMap' in the method setMaxAlpha in modules/graphics/src/main/native-prism/Renderer.c But I don't see a corresponding 'free' anywhere. I haven't dug deep enough to know if setMaxAlpha is called frequently enough that it might be the source of the leak.

I forgot to mention that the attached gradle project is configured to launch with -Xcomp using the 'run' task. I did this thinking it may reduce fluctuations in memory usage caused by compilation of hotspots. For faster startup that option can be removed. I find it easier to observe the leak on OS X with Activity Monitor.