From 7eb0cee1787ce5c4a0c91da56f2218f42db5662c Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Mon, 13 Aug 2018 13:17:25 +0200 Subject: [PATCH] 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. --- .../bytebuddy/BasicProxyFactoryImpl.java | 10 +-- .../internal/bytebuddy/ByteBuddyState.java | 71 +++++++++++++++++++ .../pojo/bytebuddy/ByteBuddyProxyHelper.java | 27 ++----- 3 files changed, 79 insertions(+), 29 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BasicProxyFactoryImpl.java b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BasicProxyFactoryImpl.java index 46ba636b43..eae143eb67 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BasicProxyFactoryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BasicProxyFactoryImpl.java @@ -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() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/ByteBuddyState.java b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/ByteBuddyState.java index 3acdf37439..0c46420472 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/ByteBuddyState.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/ByteBuddyState.java @@ -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. + *

+ * 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 groovyGetMetaClassFilter; + private final ElementMatcher virtualNotFinalizerFilter; + private final ElementMatcher 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 getGroovyGetMetaClassFilter() { + return groovyGetMetaClassFilter; + } + + public ElementMatcher getVirtualNotFinalizerFilter() { + return virtualNotFinalizerFilter; + } + + public ElementMatcher getHibernateGeneratedMethodFilter() { + return hibernateGeneratedMethodFilter; + } + + public MethodDelegation getDelegateToInterceptorDispatcherMethodDelegation() { + return delegateToInterceptorDispatcherMethodDelegation; + } + + public FieldAccessor.PropertyConfigurable getInterceptorFieldAccessor() { + return interceptorFieldAccessor; + } + } } diff --git a/hibernate-core/src/main/java/org/hibernate/proxy/pojo/bytebuddy/ByteBuddyProxyHelper.java b/hibernate-core/src/main/java/org/hibernate/proxy/pojo/bytebuddy/ByteBuddyProxyHelper.java index f94cf4b943..2e3d5fdef5 100644 --- a/hibernate-core/src/main/java/org/hibernate/proxy/pojo/bytebuddy/ByteBuddyProxyHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/proxy/pojo/bytebuddy/ByteBuddyProxyHelper.java @@ -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.>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() ) ); }