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