Skip to content

Releases: ultraq/thymeleaf-layout-dialect

Thymeleaf Layout Dialect 2.4.0

30 Mar 23:38
Compare
Choose a tag to compare
  • Allow passing of values up to layout templates using fragment parameters (#157)
  • Updated how <head> element sorting was done so that the <title> can now
    optionally be put as the first element, instead of always making it first.
    Also created 2 new sorting strategies that reflect this respecting of the
    <title> and other element positions. (#176)
  • Fixed a bug around nested elements which arose in 2.3.0 (#178)
  • Fixed a bug when self-closing <html> tags are used as a root element (#173)
  • Added an experimental option to allow developers to opt-out of the automatic
    <head> merging that normally occurs, useful if wanted to manage that
    section using other Thymeleaf processors (#165)

Thymeleaf Layout Dialect 2.3.0

11 Feb 02:48
Compare
Choose a tag to compare
  • Verification that the layout dialect plays well with Java 9 and Spring 5, involved a patch upgrade of Groovy to 2.4.13 (#161)
  • Contribution from @Vineg to add a layout:collect/data-layout-collect processor that accumulates encountered fragments of the same name. Documentation is still pending, but eager devs can take a look at the PR for more details (#166)
  • A few tool updates for linting, automated testing, and code coverage w/ Travis CI (included dropping the JDK7 build as I could no longer get a supported configuration working on Travis, but the layout dialect still aims to support Java 7 for the remainder of the 2.x releases).

Thymeleaf Layout Dialect 2.2.2

16 May 06:55
Compare
Choose a tag to compare
  • Another decorate processor root element check fix for deep hierarchies that include a th:with attribute processor (which gets mutated by the various) decoration processes) (#127)
  • Updated thymeleaf-expression-processor, which includes a null check for parsing fragment expressions, a potential fix for (#151)

Thymeleaf Layout Dialect 2.2.1

11 Mar 21:44
Compare
Choose a tag to compare
  • Fix decorate processor root element check when interacting with high-priority custom dialects (#127)

Thymeleaf Layout Dialect 2.2.0

19 Feb 00:42
Compare
Choose a tag to compare
  • Rework how titles are handled to support inline expressions in Thymeleaf 3 (#145)
  • Now that the complete title cannot be known during execution of the title pattern processor, remove the exposed "layout context" object which contained the title values (#147)
  • Implement a more accurate way to check if the decorate processor is in the root element of a template (#127)

Thymeleaf Layout Dialect 2.1.2

30 Nov 07:16
Compare
Choose a tag to compare
  • Fix insertion of elements into a <head> section that is empty, ie: <head></head> (#144)

Thymeleaf Layout Dialect 2.1.1

10 Nov 07:27
Compare
Choose a tag to compare
  • Simplify and fix the "model level" counting algorithm after a better understanding of attoparser and how it works (#143, #142)

Thymeleaf Layout Dialect 2.1.0

06 Nov 02:27
Compare
Choose a tag to compare
  • Be less strict with HTML templates that are auto-balanced by Attoparser (usually a result of not knowing which HTML elements cause auto-closing behaviours), instead only using tags that are in the original templates to influence the "model level". While this was a great tool for learning more about the HTML spec when it errors, it is more in line with how Thymeleaf behaves (#138)
  • Reveal the processed content and layout title values on the layout object (#137)
  • Huge improvements to the memory profile of the layout dialect (#102, #139)

Details of performance improvements between 2.0 and 2.1

I've uploaded the very basic web app that I was using for all of my memory tests since the first report of a memory leak in 2.0.0. GitHub repo of that project can be found here: https://github.com/ultraq/thymeleaf-layout-dialect-benchmark

It does very little, only using layout dialect features so that other things that would normally be a part of an application don't impact the tests. It does use Spring MVC however, which might be unnecessary overhead, but I have a good amount of faith in the Spring project's robustness such that I don't think it'll affect the results too badly.

That web app is started with the YourKit profiling agent attached, and object allocation profiling is also enabled. The web app is then stress tested with a simple JMeter test plan (included in the repo) that simulates concurrent users and load to exaggerate any problems in the layout dialect. At the end of the test, a forced GC is done and a memory snapshot is taken to see how the app is at rest.

Thymeleaf Layout Dialect 2.0.4

memory usage 2 0 4

Main takeaways:

  • The JMeter test took about 3 minutes to complete (started around the 30 second mark), with requests taking an average of 1.674 seconds each

  • Old generation space at 99MB

  • 35 garbage collections

  • 27 million object allocations

  • 4 seconds spent in GC

  • Several items taking over 10MB of retained memory (none of them appearing as dominators however, so are potentially GC'able, but don't seem to have been collected)

    screen shot 2016-11-06 at 1 33 32 pm

  • Majority of the object allocations taking place in the IModelExtensions.findModel closure, which uses a Groovy feature of dynamic metaclass creation

    screen shot 2016-11-06 at 1 36 02 pm

Thymeleaf Layout Dialect 2.1.0

memory usage 2 1 0-snapshot

Differences:

  • The JMeter test took about 1 minute to complete (also started around the 30 second mark), with requests taking an average of 452ms to complete (at least 3x faster)

  • Old generation space at 22MB (memory footprint 1/5th the size)

  • 21 garbage collections (40% less GCs)

  • 7.1 million object allocations (74% less objects created)

  • 1 second spent in GC (75% less time spent in GC)

  • Only 1 item taking over 10MB of retained memory (dominator profile looking mostly the same however)

    screen shot 2016-11-06 at 1 51 37 pm

  • Majority of the object allocations no longer in a Groovy dynamic meta class method, but in one of Thymeleaf's utility projects, unbescape

    screen shot 2016-11-06 at 1 54 42 pm

Changes made and lessons learned

The change that had the biggest impact to the performance profile of the layout dialect was the removal of Groovy's dynamic metaclass creation. Here's a line representative of what that is: https://github.com/ultraq/thymeleaf-layout-dialect/blob/b9f00007e438c476b3640db7c17bdc9a7c474283/Source/nz/net/ultraq/thymeleaf/models/extensions/IModelExtensions.groovy#L211 What that line did was add a property, startIndex, to the object which I would use later on in the dialect to know exactly where a model started. A similar property is added for where the model ended. This was done because, in Thymeleaf 3, it did away with DOM nodes and so it was much harder for dialects to track the things that made up an "element", instead relying on queues of what it calls "events" (text, tags, comments, anything that you write into a template) with no clear demarcation of elements.

This was a huge convenience for the code, but as shown in the object allocation profiling above, something about it incurred a massive cost in memory. This was Groovy under-the-hood code, so I don't know exactly what goes on there to provide programmers this convenience.

So I removed lines like that throughout the layout dialect, and implemented an additional step for calling code to search for the start/end of a model after the model was received. Theoretically this should have been slower because it's an additional O(n) lookup on code that already did an O(n) lookup to retrieve the model in the first place, but practically it beat out the dynamic metaclass allocation.

Lesson learned: be careful/sparing with dynamic metaclasses. The convenience they provide is a boon for programmers, but if in a critical part of the code its benefits may not outweigh the costs.

Notes, caveats, and final words

These numbers are specific to the benchmark that I ran them on, so don't expect to see improvements of a similar scale in your own app. However, this all just means that the layout dialect should now be even less of a use on your own app's memory and CPU profiles, thus allowing you to focus instead on the performance of your app rather than the performance of your libraries.

If you continue to experience performance problems though, feel free to raise an issue but also provide memory profiles if at all possible. I've actually quite enjoyed digging into and fixing up these things as I learn a lot from it in the process.

Thymeleaf Layout Dialect 2.0.5

22 Oct 22:48
Compare
Choose a tag to compare
  • Upgrade thymeleaf-expression-processor to 1.1.2, which includes a fix for multiline fragment expressions (#140)
  • Use Thymeleaf's AssignationUtils class for parsing variable declarations so that variable declarations behave the same way they do in Thymeleaf (#126)

Thymeleaf Layout Dialect 2.0.4

01 Oct 10:38
Compare
Choose a tag to compare
  • Have the layout:title-pattern processor work when using th:utext/data-th-utext on the <title> tag as well (#136)