Skip to content

Commit 02ef8c1

Browse files
committed
HHH-19230 Avoid class loader leak in enhancement and improve bytebuddy type caching efficiency
1 parent 6de6d36 commit 02ef8c1

File tree

6 files changed

+127
-166
lines changed

6 files changed

+127
-166
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.bytecode.enhance.internal.bytebuddy;
8+
9+
import net.bytebuddy.dynamic.ClassFileLocator;
10+
import net.bytebuddy.pool.TypePool;
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<EnhancementState> enhancementState = new ThreadLocal<>();
18+
19+
@Override
20+
public TypePool.Resolution find(final String name) {
21+
final EnhancementState enhancementState = getEnhancementState();
22+
if ( enhancementState != null && enhancementState.getClassName().equals( name ) ) {
23+
return enhancementState.getTypePoolResolution();
24+
}
25+
return super.find( name );
26+
}
27+
28+
EnhancementState getEnhancementState() {
29+
return enhancementState.get();
30+
}
31+
32+
void setEnhancementState(EnhancementState state) {
33+
enhancementState.set( state );
34+
}
35+
36+
void removeEnhancementState() {
37+
enhancementState.remove();
38+
}
39+
40+
static final class EnhancementState {
41+
private final String className;
42+
private final ClassFileLocator.Resolution classFileResolution;
43+
private TypePool.Resolution typePoolResolution;
44+
45+
public EnhancementState(String className, ClassFileLocator.Resolution classFileResolution) {
46+
this.className = className;
47+
this.classFileResolution = classFileResolution;
48+
}
49+
50+
public String getClassName() {
51+
return className;
52+
}
53+
54+
public ClassFileLocator.Resolution getClassFileResolution() {
55+
return classFileResolution;
56+
}
57+
58+
public TypePool.Resolution getTypePoolResolution() {
59+
return typePoolResolution;
60+
}
61+
62+
public void setTypePoolResolution(TypePool.Resolution typePoolResolution) {
63+
this.typePoolResolution = typePoolResolution;
64+
}
65+
}
66+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.bytecode.enhance.internal.bytebuddy;
8+
9+
import net.bytebuddy.dynamic.ClassFileLocator;
10+
11+
import java.io.IOException;
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 EnhancerCacheProvider cacheProvider;
19+
private final ClassFileLocator delegate;
20+
21+
public EnhancerClassFileLocator(EnhancerCacheProvider cacheProvider, ClassFileLocator delegate) {
22+
this.cacheProvider = cacheProvider;
23+
this.delegate = delegate;
24+
}
25+
26+
@Override
27+
public Resolution locate(final String name) throws IOException {
28+
final EnhancerCacheProvider.EnhancementState enhancementState = cacheProvider.getEnhancementState();
29+
if ( enhancementState != null && enhancementState.getClassName().equals( name ) ) {
30+
return enhancementState.getClassFileResolution();
31+
}
32+
return delegate.locate( name );
33+
}
34+
35+
@Override
36+
public void close() throws IOException {
37+
delegate.close();
38+
}
39+
40+
}

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

Lines changed: 21 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,21 @@
66
*/
77
package org.hibernate.bytecode.enhance.internal.bytebuddy;
88

9-
import java.util.Objects;
10-
import java.util.concurrent.ConcurrentHashMap;
11-
129
import net.bytebuddy.dynamic.ClassFileLocator;
1310
import net.bytebuddy.pool.TypePool;
1411

12+
import java.util.Objects;
13+
1514
/**
1615
* A TypePool suitable for loading user's classes,
1716
* potentially in parallel operations.
1817
*/
1918
public class ModelTypePool extends TypePool.Default implements EnhancerClassLocator {
2019

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

25-
private ModelTypePool(SafeCacheProvider cacheProvider, OverridingClassFileLocator classFileLocator, CoreTypePool parent) {
23+
private ModelTypePool(EnhancerCacheProvider cacheProvider, EnhancerClassFileLocator classFileLocator, CoreTypePool parent) {
2624
super( cacheProvider, classFileLocator, ReaderMode.FAST, parent );
2725
this.poolCache = cacheProvider;
2826
this.locator = classFileLocator;
@@ -64,7 +62,7 @@ public static EnhancerClassLocator buildModelTypePool(ClassFileLocator classFile
6462
* @return
6563
*/
6664
public static EnhancerClassLocator buildModelTypePool(ClassFileLocator classFileLocator, CoreTypePool coreTypePool) {
67-
return buildModelTypePool( classFileLocator, coreTypePool, new SafeCacheProvider() );
65+
return buildModelTypePool( classFileLocator, coreTypePool, new EnhancerCacheProvider() );
6866
}
6967

7068
/**
@@ -74,44 +72,35 @@ public static EnhancerClassLocator buildModelTypePool(ClassFileLocator classFile
7472
* @param cacheProvider
7573
* @return
7674
*/
77-
public static EnhancerClassLocator buildModelTypePool(ClassFileLocator classFileLocator, CoreTypePool coreTypePool, SafeCacheProvider cacheProvider) {
75+
static EnhancerClassLocator buildModelTypePool(ClassFileLocator classFileLocator, CoreTypePool coreTypePool, EnhancerCacheProvider cacheProvider) {
7876
Objects.requireNonNull( classFileLocator );
7977
Objects.requireNonNull( coreTypePool );
8078
Objects.requireNonNull( cacheProvider );
81-
return new ModelTypePool( cacheProvider, new OverridingClassFileLocator( classFileLocator ), coreTypePool );
82-
}
83-
84-
@Override
85-
protected Resolution doDescribe(final String name) {
86-
final Resolution resolution = resolutions.get( name );
87-
if ( resolution != null ) {
88-
return resolution;
89-
}
90-
else {
91-
return resolutions.computeIfAbsent( name, super::doDescribe );
92-
}
79+
return new ModelTypePool( cacheProvider, new EnhancerClassFileLocator( cacheProvider, classFileLocator ), coreTypePool );
9380
}
9481

9582
@Override
9683
public void registerClassNameAndBytes(final String className, final byte[] bytes) {
97-
//Very important: ensure the registered override is actually effective in case this class
98-
//was already resolved in the recent past; this could have happened for example as a side effect
99-
//of symbol resolution during enhancement of a different class, or very simply when attempting
100-
//to re-enhanced the same class - which happens frequently in WildFly because of the class transformers
101-
//being triggered concurrently by multiple parallel deployments.
102-
resolutions.remove( className );
103-
poolCache.remove( className );
104-
locator.put( className, new ClassFileLocator.Resolution.Explicit( Objects.requireNonNull( bytes ) ) );
84+
final EnhancerCacheProvider.EnhancementState currentEnhancementState = poolCache.getEnhancementState();
85+
if ( currentEnhancementState != null ) {
86+
throw new IllegalStateException( "Re-entrant enhancement is not supported: " + className );
87+
}
88+
final EnhancerCacheProvider.EnhancementState state = new EnhancerCacheProvider.EnhancementState(
89+
className,
90+
new ClassFileLocator.Resolution.Explicit( Objects.requireNonNull( bytes ) )
91+
);
92+
// Set the state first because the ClassFileLocator needs this in the doDescribe() call below
93+
poolCache.setEnhancementState( state );
94+
state.setTypePoolResolution( doDescribe( className ) );
10595
}
10696

10797
@Override
108-
public void deregisterClassNameAndBytes(final String className) {
109-
locator.remove( className );
98+
public void deregisterClassNameAndBytes(String className) {
99+
poolCache.removeEnhancementState();
110100
}
111101

112102
@Override
113103
public ClassFileLocator asClassFileLocator() {
114104
return locator;
115105
}
116-
117106
}

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

Lines changed: 0 additions & 82 deletions
This file was deleted.

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

Lines changed: 0 additions & 49 deletions
This file was deleted.

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,6 @@ public byte[] transform(
6262
catch (final Exception e) {
6363
throw new TransformerException( "Error performing enhancement of " + className, e );
6464
}
65-
finally {
66-
bytecodeProvider.resetCaches();
67-
}
6865
}
6966

7067
@Override

0 commit comments

Comments
 (0)