Hello Oli,

I apologize for the bad surprise on the migration. Do you have a
suggestion? If you have time, feel free to offer it on GitHub as a PR.
Otherwise, please create a Jira ticket so we can track this.

Thank you,
Gary

On Thu, Oct 29, 2026, 18:06 Gillespie, Oli via user <[email protected]>
wrote:

> Hi,
>
> After upgrading to 3.21.0, I saw a memory leak in my application related
> to the changes in ToStringStyle. The new 'VISITED'[1] ThreadLocal
> IdentityHashMap shows unbounded growth when used in this (admittedly
> strange) fashion.
>
> Here's a reproducer:
>
> ```
> /*
>  * curl -fLO
> https://repo1.maven.org/maven2/org/apache/commons/commons-lang3/3.21.0/commons-lang3-3.21.0.jar
>  * java -cp commons-lang3-3.21.0.jar VisitedLeak.java // leaks
>  * // Invocation 1000: REGISTRY.size=1 VISITED.size=1000000
>  * java -cp commons-lang3-3.21.0.jar VisitedLeak.java clean // control, no
> leak
>  */
> import org.apache.commons.lang3.builder.*;
> import java.lang.reflect.Field;
> import java.util.*;
>
> public class VisitedLeak {
>     static class Item { int id; }
>     static class Graph { List<Object> items = new ArrayList<>() {{ for
> (int i = 0; i < 999; i++) add(new Object()); }}; }
>
>     static int size(String field) throws Exception {
>         Field f = ToStringStyle.class.getDeclaredField(field);
> f.setAccessible(true);
>         return ((Map<?, ?>) ((ThreadLocal<?>) f.get(null)).get()).size();
>     }
>
>     public static void main(String[] args) throws Exception {
>         if (args.length == 0) new ReflectionToStringBuilder(new Graph(),
> new RecursiveToStringStyle()); // start leak
>         for (int i = 0; i <= 1000; i++) {
>             if (i % 100 == 0) System.out.printf("Invocation %d:
> REGISTRY.size=%d VISITED.size=%d%n", i, size("REGISTRY"), size("VISITED"));
>             new ReflectionToStringBuilder(new Graph(), new
> RecursiveToStringStyle()).toString();
>         }
>     }
> }
> ```
> prints `Invocation 1000: REGISTRY.size=1 VISITED.size=1000000`,
> demonstrating the leak.
>
> In my application this was because of some awkward logging code:
> `log.debug("...", new ReflectionToStringBuilder(...))`. toString() is not
> called if debug logging is not enabled, starting the leak. Easy fix on my
> side, but reporting in case you consider this a bug, or for anyone else
> that might run into it.
>
> Oli
>
> [1]
> https://github.com/apache/commons-lang/blob/master/src/main/java/org/apache/commons/lang3/builder/ToStringStyle.java#L607
>
>
>
> Amazon Development Centre (London) Ltd. Registered in England and Wales
> with registration number 04543232 with its registered office at 1
> Principal Place
> <https://www.google.com/maps/search/1+Principal+Place?entry=gmail&source=g>,
> Worship Street, London EC2A 2FA, United Kingdom.
>
>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [email protected]
> For additional commands, e-mail: [email protected]
>
>

Reply via email to