Skip to content

Avoid retaining class files in annotation metadata - #37112

Merged
bclozel merged 1 commit into
spring-projects:mainfrom
perovic:classfile-annotation-source-retention
Aug 5, 2026
Merged

Avoid retaining class files in annotation metadata#37112
bclozel merged 1 commit into
spring-projects:mainfrom
perovic:classfile-annotation-source-retention

Conversation

@perovic

@perovic perovic commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

This is the Class-File versus ASM metadata overhead case from #36737, reported with a reproducer in #37111. On Java 24+ MetadataReaderFactoryDelegate selects the Class-File reader, which retains considerably more heap than the ASM one for the same classes.

ClassFileAnnotationDelegate keeps the raw java.lang.classfile.Annotation as the MergedAnnotation source. That annotation holds a Utf8Entry, so retaining the metadata of an annotated class also retains its class file byte[], the parsed constant pool, and the class model. This stores the declaring class name instead, which is what SimpleAnnotationMetadataReadingVisitor.Source holds on the ASM path.

Measured with the reproducer (Temurin 25.0.3, spring-core 7.0.8):

retained heap
SimpleMetadataReaderFactory (ASM) 12.3 MB
ClassFileMetadataReaderFactory before 84.5 MB
ClassFileMetadataReaderFactory after 16.7 MB

In a large application's test context this took the retained size of a single DefaultListableBeanFactory from 128.4 MB to 53.4 MB. Forcing the ASM reader in the same build, by copying MetadataReaderFactoryDelegate into the project as suggested here, gives 46.1 MB.

The difference that is left is not addressed here. ClassFileMethodMetadata holds AccessFlags and MethodTypeDesc objects and ClassFileAnnotationMetadata one AccessFlags, where SimpleMethodMetadata and SimpleAnnotationMetadata keep plain int and String values instead. #36978 already makes that change for ClassFileMethodMetadata. Once both are changed, the reproducer matches the ASM baseline of 12.3 MB and the test context mentioned above comes to 46.9 MB.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Aug 4, 2026
@bclozel bclozel self-assigned this Aug 5, 2026
@bclozel
bclozel force-pushed the classfile-annotation-source-retention branch from ef51942 to 2706399 Compare August 5, 2026 08:07
ClassFileAnnotationDelegate passed the raw java.lang.classfile
Annotation to MergedAnnotation.of() as the annotation source. That
annotation holds a Utf8Entry, so retaining the metadata of an
annotated class also retained its class file byte[], the parsed
constant pool, and the class model.

Store the declaring class name instead, as the ASM variant does.

See spring-projectsgh-37111

Signed-off-by: Boris Perović <boris.perovic@sysdig.com>
@bclozel
bclozel force-pushed the classfile-annotation-source-retention branch from 2706399 to b556766 Compare August 5, 2026 08:07
@bclozel bclozel added in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Aug 5, 2026
@bclozel
bclozel merged commit c17b4ad into spring-projects:main Aug 5, 2026
1 check passed
@bclozel

bclozel commented Aug 5, 2026

Copy link
Copy Markdown
Member

Thanks @perovic !

@bclozel bclozel added this to the 7.0.9 milestone Aug 5, 2026
@perovic
perovic deleted the classfile-annotation-source-retention branch August 5, 2026 11:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants