> On Aug 10, 2025, at 2:29 PM, Johannes Döbler <j...@civilian-framework.org> 
> 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.

> 
> 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
>> 
> 

Reply via email to