> On Aug 11, 2025, at 9:44 AM, Steven Schlansker <[email protected]>
> wrote:
>
>>
>> On Aug 10, 2025, at 2:29 PM, Johannes Döbler <[email protected]>
>> wrote:
>>
>> Hi Steven,
>>
>> I think you are onto something. Given this example
>>
>>> public class TypeParamsDemo {
>>> public static void main(String[] args) throws Exception {
>>> Method method = TypeParamsDemo.class.getMethod("getList", Set.class);
>>> TypeVariable<Method>[] tp = method.getTypeParameters();
>>> }
>>>
>>> public <T extends Number> List<T> getList(Set<T> set) {
>>> return new ArrayList<>(set);
>>> }
>>> }
>>
>> Then:
>> tp[0].bounds[0].path[0].name is "java.lang.Number" and is a newly created
>> String parsed from the type signature
>> "<T:Ljava/lang/Number;>(Ljava/util/Set<TT;>;)Ljava/util/List<TT;>;".
>> The result of method.getTypeParameters() is cached in Method.genericInfo, so
>> once initialized by a caller (e.g. by Jackson/REST easy) it won't go away.
>> Imho interning the class name should be fine since it refers to an existing
>> class.
>>
>> Best
>> Johannes
>>
>
> Thank you for your analysis Johannes.
> I've opened https://github.com/openjdk/jdk/pull/26730 with my proposed change.
>
It seems that I need a bug issue open in order to proceed any further.
Is anyone able to create a bug report and potentially sponsor this change?
Thank you!
Steven
>>
>> On 09/08/2025 01:10, Steven Schlansker wrote:
>>> Hello core-libs-dev, happy Friday!
>>>
>>> While diagnosing an out of memory situation in our application, I noticed a
>>> surprising source of memory usage.
>>> While this is not so severe to actually cause our OOM, it seems wasteful
>>> and I thought to bring it to your attention.
>>>
>>> We use reflection-based technologies like Jackson JSON and RESTEasy that
>>> retrieve generic type information from many of our classes.
>>> Our profiler provides diagnostics of wasted space due to duplicate objects,
>>> particularly Strings.
>>>
>>> The analysis highlights many thousands of copies of String instances
>>> holding the full name of a class, e.g. "java.util.Optional"
>>> or "com.mycompany.Id". The path to GC route looks like:
>>>
>>> String <- sun.reflect.generics.tree.SimpleClassTypeSignature <- Object[] <-
>>> ArrayList <- ClassTypeSignature <- MethodTypeSignature <- MethodRepository
>>> <- Method
>>>
>>> Seeing how these SimpleClassTypeSignature instances are created, it looks
>>> like they come from the sun.reflect.generics.parser.SignatureParser which
>>> calls
>>> `input.substring(mark, index)`, possibly with a call to `replace('/', '.')`
>>> to munge the package name. In all but the simplest of cases, this will
>>> return a new String
>>> for every call.
>>>
>>> Since this String is representing a Class name, the cardinality should by
>>> its nature be very low. For each unique type, there will be many methods
>>> referring to it.
>>> Additionally, this generic information is lazy-loaded at most once per
>>> Method object.
>>>
>>> Therefore, SimpleClassTypeSignature.n seems like a natural place to apply
>>> String.intern(), for example changing:
>>>
>>> public static SimpleClassTypeSignature make(String n,
>>> boolean dollar,
>>> TypeArgument[] tas){
>>> return new SimpleClassTypeSignature(n, dollar, tas);
>>> }
>>>
>>> to intern the name:
>>>
>>> public static SimpleClassTypeSignature make(String n,
>>> boolean dollar,
>>> TypeArgument[] tas){
>>> return new SimpleClassTypeSignature(n.intern(), dollar, tas);
>>> }
>>>
>>> With any luck, maybe this can even share the same string instance as the
>>> class itself uses.
>>> Am I correct in thinking this would be a moderately nice improvement, for a
>>> relatively cheap cost?
>>> Or perhaps there's some reason this isn't a good idea?
>>>
>>> Thank you for your thoughts on the subject,
>>> Steven