HHH-12907 Avoid creating too many ByteBuddy objects

This commit should reduce the garbage collection pressure as reported at
https://github.com/raphw/byte-buddy/issues/515.
This commit is contained in:
Guillaume Smet 2018-08-13 13:17:25 +02:00
parent a934a03a20
commit 34f58e91c6
3 changed files with 79 additions and 29 deletions

View File

@ -20,10 +20,6 @@ import net.bytebuddy.NamingStrategy;
import net.bytebuddy.TypeCache;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
import net.bytebuddy.implementation.FieldAccessor;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import net.bytebuddy.matcher.ElementMatchers;
public class BasicProxyFactoryImpl implements BasicProxyFactory {
@ -47,10 +43,10 @@ public class BasicProxyFactoryImpl implements BasicProxyFactory {
.subclass( superClass == null ? Object.class : superClass, ConstructorStrategy.Default.DEFAULT_CONSTRUCTOR )
.implement( interfaces == null ? NO_INTERFACES : interfaces )
.defineField( ProxyConfiguration.INTERCEPTOR_FIELD_NAME, ProxyConfiguration.Interceptor.class, Visibility.PRIVATE )
.method( ElementMatchers.isVirtual().and( ElementMatchers.not( ElementMatchers.isFinalizer() ) ) )
.intercept( MethodDelegation.to( ProxyConfiguration.InterceptorDispatcher.class ) )
.method( byteBuddyState.getProxyDefinitionHelpers().getVirtualNotFinalizerFilter() )
.intercept( byteBuddyState.getProxyDefinitionHelpers().getDelegateToInterceptorDispatcherMethodDelegation() )
.implement( ProxyConfiguration.class )
.intercept( FieldAccessor.ofField( ProxyConfiguration.INTERCEPTOR_FIELD_NAME ).withAssigner( Assigner.DEFAULT, Assigner.Typing.DYNAMIC ) )
.intercept( byteBuddyState.getProxyDefinitionHelpers().getInterceptorFieldAccessor() )
);
this.interceptor = new PassThroughInterceptor( proxyClass.getName() );
}

View File

@ -6,6 +6,13 @@
*/
package org.hibernate.bytecode.internal.bytebuddy;
import static net.bytebuddy.matcher.ElementMatchers.isFinalizer;
import static net.bytebuddy.matcher.ElementMatchers.isSynthetic;
import static net.bytebuddy.matcher.ElementMatchers.isVirtual;
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import static org.hibernate.internal.CoreLogging.messageLogger;
import java.io.File;
@ -20,17 +27,23 @@ import java.util.function.Function;
import org.hibernate.HibernateException;
import org.hibernate.bytecode.spi.BasicProxyFactory;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.proxy.ProxyConfiguration;
import org.hibernate.proxy.ProxyFactory;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.TypeCache;
import net.bytebuddy.asm.AsmVisitorWrapper.ForDeclaredMethods;
import net.bytebuddy.asm.MemberSubstitution;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.DynamicType.Unloaded;
import net.bytebuddy.dynamic.loading.ClassInjector;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.dynamic.scaffold.TypeValidation;
import net.bytebuddy.implementation.FieldAccessor;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.pool.TypePool;
@ -52,6 +65,8 @@ public final class ByteBuddyState {
private final ForDeclaredMethods getDeclaredMethodMemberSubstitution;
private final ForDeclaredMethods getMethodMemberSubstitution;
private final ProxyDefinitionHelpers proxyDefinitionHelpers;
/**
* It will be easier to maintain the cache and its state when it will no longer be static
* in Hibernate ORM 6+.
@ -75,6 +90,8 @@ public final class ByteBuddyState {
this.getDeclaredMethodMemberSubstitution = null;
this.getMethodMemberSubstitution = null;
}
this.proxyDefinitionHelpers = new ProxyDefinitionHelpers();
}
/**
@ -135,6 +152,17 @@ public final class ByteBuddyState {
return make( typePool, builder ).getBytes();
}
/**
* Returns the proxy definition helpers to reuse when defining proxies.
* <p>
* These elements are shared as they are immutable.
*
* @return The proxy definition helpers.
*/
public ProxyDefinitionHelpers getProxyDefinitionHelpers() {
return proxyDefinitionHelpers;
}
/**
* Wipes out all known caches used by ByteBuddy. This implies it might trigger the need
* to re-create some helpers if used at runtime, especially as this state is shared by
@ -276,4 +304,47 @@ public final class ByteBuddyState {
}
}
}
/**
* Shared proxy definition helpers. They are immutable so we can safely share them.
*/
public static class ProxyDefinitionHelpers {
private final ElementMatcher<? super MethodDescription> groovyGetMetaClassFilter;
private final ElementMatcher<? super MethodDescription> virtualNotFinalizerFilter;
private final ElementMatcher<? super MethodDescription> hibernateGeneratedMethodFilter;
private final MethodDelegation delegateToInterceptorDispatcherMethodDelegation;
private final FieldAccessor.PropertyConfigurable interceptorFieldAccessor;
private ProxyDefinitionHelpers() {
this.groovyGetMetaClassFilter = isSynthetic().and( named( "getMetaClass" )
.and( returns( td -> "groovy.lang.MetaClass".equals( td.getName() ) ) ) );
this.virtualNotFinalizerFilter = isVirtual().and( not( isFinalizer() ) );
this.hibernateGeneratedMethodFilter = nameStartsWith( "$$_hibernate_" ).and( isVirtual() );
this.delegateToInterceptorDispatcherMethodDelegation = MethodDelegation
.to( ProxyConfiguration.InterceptorDispatcher.class );
this.interceptorFieldAccessor = FieldAccessor.ofField( ProxyConfiguration.INTERCEPTOR_FIELD_NAME )
.withAssigner( Assigner.DEFAULT, Assigner.Typing.DYNAMIC );
}
public ElementMatcher<? super MethodDescription> getGroovyGetMetaClassFilter() {
return groovyGetMetaClassFilter;
}
public ElementMatcher<? super MethodDescription> getVirtualNotFinalizerFilter() {
return virtualNotFinalizerFilter;
}
public ElementMatcher<? super MethodDescription> getHibernateGeneratedMethodFilter() {
return hibernateGeneratedMethodFilter;
}
public MethodDelegation getDelegateToInterceptorDispatcherMethodDelegation() {
return delegateToInterceptorDispatcherMethodDelegation;
}
public FieldAccessor.PropertyConfigurable getInterceptorFieldAccessor() {
return interceptorFieldAccessor;
}
}
}

View File

@ -6,17 +6,8 @@
*/
package org.hibernate.proxy.pojo.bytebuddy;
import static net.bytebuddy.matcher.ElementMatchers.isFinalizer;
import static net.bytebuddy.matcher.ElementMatchers.isSynthetic;
import static net.bytebuddy.matcher.ElementMatchers.isVirtual;
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import static org.hibernate.internal.CoreLogging.messageLogger;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
@ -28,24 +19,16 @@ import java.util.Set;
import org.hibernate.HibernateException;
import org.hibernate.bytecode.internal.bytebuddy.ByteBuddyState;
import org.hibernate.cfg.Environment;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.ProxyConfiguration;
import org.hibernate.proxy.ProxyFactory;
import org.hibernate.type.CompositeType;
import net.bytebuddy.NamingStrategy;
import net.bytebuddy.TypeCache;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.dynamic.DynamicType.Unloaded;
import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
import net.bytebuddy.implementation.FieldAccessor;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.SuperMethodCall;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
public class ByteBuddyProxyHelper implements Serializable {
@ -69,17 +52,17 @@ public class ByteBuddyProxyHelper implements Serializable {
key.addAll( Arrays.<Class<?>>asList( interfaces ) );
return byteBuddyState.loadProxy( persistentClass, new TypeCache.SimpleKey(key), byteBuddy -> byteBuddy
.ignore( isSynthetic().and( named( "getMetaClass" ).and( returns( td -> "groovy.lang.MetaClass".equals( td.getName() ) ) ) ) )
.ignore( byteBuddyState.getProxyDefinitionHelpers().getGroovyGetMetaClassFilter() )
.with( new NamingStrategy.SuffixingRandom( PROXY_NAMING_SUFFIX, new NamingStrategy.SuffixingRandom.BaseNameResolver.ForFixedValue( persistentClass.getName() ) ) )
.subclass( interfaces.length == 1 ? persistentClass : Object.class, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING )
.implement( (Type[]) interfaces )
.method( isVirtual().and( not( isFinalizer() ) ) )
.intercept( MethodDelegation.to( ProxyConfiguration.InterceptorDispatcher.class ) )
.method( nameStartsWith( "$$_hibernate_" ).and( isVirtual() ) )
.method( byteBuddyState.getProxyDefinitionHelpers().getVirtualNotFinalizerFilter() )
.intercept( byteBuddyState.getProxyDefinitionHelpers().getDelegateToInterceptorDispatcherMethodDelegation() )
.method( byteBuddyState.getProxyDefinitionHelpers().getHibernateGeneratedMethodFilter() )
.intercept( SuperMethodCall.INSTANCE )
.defineField( ProxyConfiguration.INTERCEPTOR_FIELD_NAME, ProxyConfiguration.Interceptor.class, Visibility.PRIVATE )
.implement( ProxyConfiguration.class )
.intercept( FieldAccessor.ofField( ProxyConfiguration.INTERCEPTOR_FIELD_NAME ).withAssigner( Assigner.DEFAULT, Assigner.Typing.DYNAMIC ) )
.intercept( byteBuddyState.getProxyDefinitionHelpers().getInterceptorFieldAccessor() )
);
}