Skip to content

Commit c5aaabf

Browse files
committed
HHH-19230 Avoid class loader leak in enhancement and improve bytebuddy type caching efficiency
1 parent f9aeb12 commit c5aaabf

File tree

6 files changed

+103
-158
lines changed

6 files changed

+103
-158
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* SPDX-License-Identifier: LGPL-2.1-or-later
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.bytecode.enhance.internal.bytebuddy;
6+
7+
import net.bytebuddy.pool.TypePool;
8+
9+
import java.util.AbstractMap;
10+
import java.util.Map;
11+
12+
/**
13+
* A simple cache provider that allows overriding the resolution for the class that is currently being enhanced.
14+
*/
15+
final class EnhancerCacheProvider extends TypePool.CacheProvider.Simple {
16+
17+
private final ThreadLocal<Map.Entry<String, TypePool.Resolution>> enhancedTypeResolution = new ThreadLocal<>();
18+
19+
@Override
20+
public TypePool.Resolution find(final String name) {
21+
final Map.Entry<String, TypePool.Resolution> entry = enhancedTypeResolution.get();
22+
if ( entry != null && entry.getKey().equals( name ) ) {
23+
return entry.getValue();
24+
}
25+
return super.find( name );
26+
}
27+
28+
@Override
29+
public void clear() {
30+
super.clear();
31+
enhancedTypeResolution.remove();
32+
}
33+
34+
public void registerEnhancedType(final String className, TypePool.Resolution resolution) {
35+
enhancedTypeResolution.set( new AbstractMap.SimpleEntry<>( className, resolution ) );
36+
}
37+
38+
public void deregisterEnhancedType(final String name) {
39+
enhancedTypeResolution.remove();
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* SPDX-License-Identifier: LGPL-2.1-or-later
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.bytecode.enhance.internal.bytebuddy;
6+
7+
import net.bytebuddy.dynamic.ClassFileLocator;
8+
9+
import java.io.IOException;
10+
import java.util.AbstractMap;
11+
import java.util.Map;
12+
13+
/**
14+
* A delegating ClassFileLocator that allows overriding the resolution for the class that is currently being enhanced.
15+
*/
16+
final class EnhancerClassFileLocator implements ClassFileLocator {
17+
18+
private final ThreadLocal<Map.Entry<String, ClassFileLocator.Resolution>> enhancedTypeResolution = new ThreadLocal<>();
19+
private final ClassFileLocator delegate;
20+
21+
public EnhancerClassFileLocator(ClassFileLocator delegate) {
22+
this.delegate = delegate;
23+
}
24+
25+
@Override
26+
public Resolution locate(final String name) throws IOException {
27+
final Map.Entry<String, ClassFileLocator.Resolution> entry = enhancedTypeResolution.get();
28+
if ( entry != null && entry.getKey().equals( name ) ) {
29+
return entry.getValue();
30+
}
31+
return delegate.locate( name );
32+
}
33+
34+
@Override
35+
public void close() throws IOException {
36+
try {
37+
delegate.close();
38+
}
39+
finally {
40+
enhancedTypeResolution.remove();
41+
}
42+
}
43+
44+
public void registerEnhancedType(final String className, ClassFileLocator.Resolution resolution) {
45+
enhancedTypeResolution.set( new AbstractMap.SimpleEntry<>( className, resolution ) );
46+
}
47+
48+
public void deregisterEnhancedType(final String name) {
49+
enhancedTypeResolution.remove();
50+
}
51+
52+
}

hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/ModelTypePool.java

+10-28
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
package org.hibernate.bytecode.enhance.internal.bytebuddy;
66

77
import java.util.Objects;
8-
import java.util.concurrent.ConcurrentHashMap;
98

109
import net.bytebuddy.dynamic.ClassFileLocator;
1110
import net.bytebuddy.pool.TypePool;
@@ -16,11 +15,10 @@
1615
*/
1716
public class ModelTypePool extends TypePool.Default implements EnhancerClassLocator {
1817

19-
private final ConcurrentHashMap<String, Resolution> resolutions = new ConcurrentHashMap<>();
20-
private final OverridingClassFileLocator locator;
21-
private final SafeCacheProvider poolCache;
18+
private final EnhancerClassFileLocator locator;
19+
private final EnhancerCacheProvider poolCache;
2220

23-
private ModelTypePool(SafeCacheProvider cacheProvider, OverridingClassFileLocator classFileLocator, CoreTypePool parent) {
21+
private ModelTypePool(EnhancerCacheProvider cacheProvider, EnhancerClassFileLocator classFileLocator, CoreTypePool parent) {
2422
super( cacheProvider, classFileLocator, ReaderMode.FAST, parent );
2523
this.poolCache = cacheProvider;
2624
this.locator = classFileLocator;
@@ -62,7 +60,7 @@ public static EnhancerClassLocator buildModelTypePool(ClassFileLocator classFile
6260
* @return
6361
*/
6462
public static EnhancerClassLocator buildModelTypePool(ClassFileLocator classFileLocator, CoreTypePool coreTypePool) {
65-
return buildModelTypePool( classFileLocator, coreTypePool, new SafeCacheProvider() );
63+
return buildModelTypePool( classFileLocator, coreTypePool, new EnhancerCacheProvider() );
6664
}
6765

6866
/**
@@ -72,39 +70,23 @@ public static EnhancerClassLocator buildModelTypePool(ClassFileLocator classFile
7270
* @param cacheProvider
7371
* @return
7472
*/
75-
public static EnhancerClassLocator buildModelTypePool(ClassFileLocator classFileLocator, CoreTypePool coreTypePool, SafeCacheProvider cacheProvider) {
73+
static EnhancerClassLocator buildModelTypePool(ClassFileLocator classFileLocator, CoreTypePool coreTypePool, EnhancerCacheProvider cacheProvider) {
7674
Objects.requireNonNull( classFileLocator );
7775
Objects.requireNonNull( coreTypePool );
7876
Objects.requireNonNull( cacheProvider );
79-
return new ModelTypePool( cacheProvider, new OverridingClassFileLocator( classFileLocator ), coreTypePool );
80-
}
81-
82-
@Override
83-
protected Resolution doDescribe(final String name) {
84-
final Resolution resolution = resolutions.get( name );
85-
if ( resolution != null ) {
86-
return resolution;
87-
}
88-
else {
89-
return resolutions.computeIfAbsent( name, super::doDescribe );
90-
}
77+
return new ModelTypePool( cacheProvider, new EnhancerClassFileLocator( classFileLocator ), coreTypePool );
9178
}
9279

9380
@Override
9481
public void registerClassNameAndBytes(final String className, final byte[] bytes) {
95-
//Very important: ensure the registered override is actually effective in case this class
96-
//was already resolved in the recent past; this could have happened for example as a side effect
97-
//of symbol resolution during enhancement of a different class, or very simply when attempting
98-
//to re-enhanced the same class - which happens frequently in WildFly because of the class transformers
99-
//being triggered concurrently by multiple parallel deployments.
100-
resolutions.remove( className );
101-
poolCache.remove( className );
102-
locator.put( className, new ClassFileLocator.Resolution.Explicit( Objects.requireNonNull( bytes ) ) );
82+
locator.registerEnhancedType( className, new ClassFileLocator.Resolution.Explicit( Objects.requireNonNull( bytes ) ) );
83+
poolCache.registerEnhancedType( className, doDescribe( className ) );
10384
}
10485

10586
@Override
10687
public void deregisterClassNameAndBytes(final String className) {
107-
locator.remove( className );
88+
poolCache.deregisterEnhancedType( className );
89+
locator.deregisterEnhancedType( className );
10890
}
10991

11092
@Override

hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/OverridingClassFileLocator.java

-80
This file was deleted.

hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/SafeCacheProvider.java

-47
This file was deleted.

hibernate-core/src/main/java/org/hibernate/jpa/internal/enhance/EnhancingClassTransformerImpl.java

-3
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,6 @@ public byte[] transform(
6060
catch (final Exception e) {
6161
throw new TransformerException( "Error performing enhancement of " + className, e );
6262
}
63-
finally {
64-
bytecodeProvider.resetCaches();
65-
}
6663
}
6764

6865
@Override

0 commit comments

Comments
 (0)