javafx.scene.shape.Path (memory) inefficient PathElements

Tom Schindl tom.schindl at bestsolution.at
Wed May 24 10:42:39 UTC 2017


Hi,

I created:
- https://bugs.openjdk.java.net/browse/JDK-8180935
- https://bugs.openjdk.java.net/browse/JDK-8180938

I'll work on a showcase to find out how much memory one can save.

Tom

On 04.05.17 23:33, Jim Graham wrote:
> Hi Tom,
> 
> Those look like good suggestions.  I would file bugs in JBS and create
> them separately:
> 
> - Bug for lazy property creation in path elements
> - Feature request for lower-memory paths
> 
> Did you benchmark how much the lazy properties, on their own, would save
> your application?
> 
>                 ...jim
> 
> On 5/4/17 2:22 PM, Tom Schindl wrote:
>> Hi,
>>
>> We are currently working on a PDF-Rendering library in JavaFX and we
>> need to draw glyphs using the JavaFX Path API (there are multiple
>> reasons why we don't use the Text-Node and or Canvas).
>>
>> When drawing a page full of Text this means that we have a Path-Object
>> with gazillions of MoveTo and CubicCurveTo elements who sum up to 30MB
>> just to represent them in the SG because PathElements store their
>> information in properties and forcefully intialize them in their
>> constructor.
>>
>> The only public API to work around this problem is to construct a
>> StringBuffer and use SVGPath which means:
>> * it takes time to construct the SVG-Path-String
>> * it takes time to parse the SVG-Path-String in JavaFX
>>
>> As an experiment (and because we are still on Java8 we can easily do
>> that) was that we created our own Shape-Subclass who:
>> * uses floats (there's no reason to use double in the SG when the
>>   backing API - Path2D - is float based)
>> * passes the floats directly to the Path2D/NGPath API
>>
>> Guess what: We now need 2.5MB / page which means 27.5MB is the overhead
>> added by the current Path-API - ouch!
>>
>> I think a fairly low hanging memory optimization for the PathElement
>> would be to create properties lazy (only if someone access the property).
>>
>> For MoveTo this would mean the following minimal change (eg for the
>> x-value):
>>
>> private DoubleProperty x;
>> private double _x;
>>
>> public final void setX(double value) {
>>   if (x != null) {
>>      xProperty().set(value);
>>   } else {
>>      _x = value;
>>      u();
>>   }
>> }
>>
>> public final double getX() {
>>   return x == null ? _x : x.get();
>> }
>>
>> public final DoubleProperty xProperty() {
>>   if (x == null) {
>>     x = new DoublePropertyBase(_x) {
>>
>>       @Override
>>       public void invalidated() {
>>         u();
>>       }
>>
>>       @Override
>>       public Object getBean() {
>>         return MoveTo.this;
>>       }
>>
>>       @Override
>>       public String getName() {
>>         return "x";
>>       }
>>    };
>>   }
>>   return x;
>> }
>>
>> I guess 99% of the code out there never access the Property so the small
>> footprint added by the primitive field is justifiable.
>>
>> This still has the overhead of all the needless PathElement objects
>> hanging around so another idea is to have a 3rd SG-Path-Type who
>> strictly uses the float-Primitives with a similar API to Path2D (in fact
>> it only acts as a public API to Path2D).
>>
>> Thoughts?
>>
>> Tom
>>


-- 
Thomas Schindl, CTO
BestSolution.at EDV Systemhaus GmbH
Eduard-Bodem-Gasse 5-7, A-6020 Innsbruck
http://www.bestsolution.at/
Reg. Nr. FN 222302s am Firmenbuchgericht Innsbruck


More information about the openjfx-dev mailing list