Mapping Classloader: creates duplicate classes when limit is reached

Description

See also: https://dev.lucee.org/t/classloading-steadily-increases-lucee-5-4-3-x/13155

We recently upgraded from Lucee 5.3.3 to 5.4.3 and are now seeing our meta space (off-heap) gradually increase over time until this eventually causes problems. Inspecting the classes loaded using jcmd <pid> GC.class_stats shows a lot of duplicate classes for cfc and cfm templates. We have inspect templates never and this is in a containerised environment where the files never change during the life of the application running.

image

image1876×841 78.9 KB

 

image

image1148×532 101 KB

 

My suspicion here is that this is caused by the following commit https://github.com/lucee/Lucee/commit/d1ec9391cd8a35f4aba285e26fb617674edaab1c but also https://luceeserver.atlassian.net/browse/LDEV-2105. In particular, a change to MappingImpl where a seemingly abitrary, and hardcoded, limit was set individually on CFC and CFM templates in the class loader(s) for the mapping:

Old limit for sum of all templates: 6,783 (introduced in https://luceeserver.atlassian.net/browse/LDEV-2105)
New limit(s): 3,000 cfc templates, 2,000 cfm templates stored in separate class loaders

Our application has a mapping with over 3,000 cfc templates and over 2,000 cfm templates. However, it has under 6783 in total which might explain why we did not see this issue with 5.3.3 but presumably / possibly the issue was still there.

Playing around with a local build I am able to reproduce this fairly quickly by hardcoding the max numbers of templates in the MappingImpl.java class to be something very small (i.e. 200 for both CFCs and CFMs). After loading the application and loading a handful of pages, I am able to see > 1,000 duplicate classes when running jcmd <PID> GC.class_stats. When I increase the limits to well over their existing values, I do not see these duplicates.

My question here I think is where do these limit numbers come from and could/should they be configurable?

This of course may not be the cause at all, but it looks like a very likely candidate given the numbers we see and the slowish rate at which we start to see these problems in our applications.

Environment

None

Attachments

2
  • 31 Oct 2023, 11:45 am
  • 31 Oct 2023, 11:45 am

Activity

Show:

Michael Offner 7 November 2023 at 16:18

i have further improved the version based on the input of my load tests

Michael Offner 7 November 2023 at 08:30

i have improved the current solution, added a cleanup process to the controller that removes the oldest records.

Michael Offner 7 November 2023 at 08:28

i changed completely how the PhysicalClassloader is handled, the new version does use one classloader for every template, but update it for every change to that template. All this classloaders are hold in s Soft reference pool (mean GC can remove them). So there is no fix limit anymore.

In my test this works fine so far, but it need some more load testing and closer comparsion to the old version.

this is how i test

setting requesttimeout=100000; loop from="1" to="1000" index="idy" { loop from="1" to="500000" index="idx" { fileWrite("testxx/inc#idx#.cfm",repeatstring( '<'&'cfset x=("#idx#-#idy#-#now()#")>',100)); include "testxx/inc#idx#.cfm" ; str=repeatstring( ' x=("#idx#-#idy#-#now()#");',100) fileWrite("testxx/CFC#idx#.cfc", 'component {#str# function test() {return ("#idx#-#idy#-:#now()#");} }'); createObject("component","testxx.CFC#idx#").test(); } sleep(100); }

by changing the loop “to“ you can influence how many new code vs code change you have.

Fixed

Details

Assignee

Reporter

Priority

Labels

Fix versions

New Issue warning screen

Before you create a new Issue, please post to the mailing list first https://dev.lucee.org

Once the issue has been verified, one of the Lucee team will ask you to file an issue

Created 31 October 2023 at 11:45
Updated 3 days ago
Resolved 15 May 2024 at 09:15

Flag notifications