byte-buddy
byte-buddy copied to clipboard
bytebuddy is throwing java.lang.OutOfMemoryError: Metaspace when proxy classes are created
We are creating the proxy class using the adapter factory like below code snippet
private static final AdapterMethodInterceptor ADAPTER_METHOD_INTERCEPTOR = new AdapterMethodInterceptor();
T adapter = new ByteBuddy()
.subclass(clazz)
.method((ElementMatchers.not(ElementMatchers.isNative())))
.intercept(MethodDelegation.to(ADAPTER_METHOD_INTERCEPTOR))
.make() .load(AdapterFactory.class.getClassLoader(),ClassLoadingStrategy.Default.INJECTION.with(AdapterFactory.class.getProtectionDomain())
.getLoaded().newInstance();
adapter.setAdaptee(adaptee);
public static class AdapterMethodInterceptor {
@RuntimeType
public Object intercept(@This Adapter<?> adapter,@Origin Method adapterMethod,@AllArguments Object[] args,@SuperMethod(nullIfImpossible = true) Method superMethod) throws Throwable {
if (!Modifier.isAbstract(adapterMethod.getModifiers()) && !Modifier.isNative(adapterMethod.getModifiers())) {
return superMethod.invoke(adapter, args);
} else if (adapterMethod.getName().equals("adapt")) {
AdapteeInvoker adapteeInvoker = (AdapteeInvoker) args[0];
Object adaptee = args[1];
Method adapteeMethod = (Method) args[2];
Object[] invocationArgs = (Object[]) args[3];
return adapteeInvoker.invoke(adaptee, adapteeMethod, invocationArgs);
} else if (adapterMethod.getName().equals("handleNullAdaptee")) {
throw new IllegalStateException(MessageBundle.localizeMessage(MsgKey.UTIL_NULL_ADAPTEE, adapter.getClass().getName()));
}
if (adapter.getAdaptee() == null) {
adapter.handleNullAdaptee();
}
Class<?> adapteeClass = adapter.getAdaptee().getClass();
Method adapteeMethod = adapteeClass.getMethod(adapterMethod.getName(), adapterMethod.getParameterTypes());
adapteeMethod.setAccessible(true);
if (!adapterMethod.getReturnType().isAssignableFrom(adapteeMethod.getReturnType())) {
throw new NoSuchMethodException(adapterMethod.toString());
}
return adapter.adapt(new AdapteeInvokerImpl(), adapter.getAdaptee(), adapteeMethod, args);
}
}
public static class AdapteeInvokerImpl implements AdapteeInvoker {
@Override
public Object invoke(Object adaptee, Method adapteeMethod, Object[] args) throws Throwable {
try {
return adapteeMethod.invoke(adaptee, args);
} catch (Exception e) {
throw e.getCause();
}
}
}
When we try to run the program to create the proxy classes, we are getting below error and all the java thread are running infinitely. In the program it calls multiple times to create the proxy classes.
Caused by: java.lang.IllegalStateException: java.lang.OutOfMemoryError: Metaspace at net.bytebuddy.dynamic.loading.ClassInjector$UsingReflection$Dispatcher$Direct.defineClass(ClassInjector.java:689) at net.bytebuddy.dynamic.loading.ClassInjector$UsingReflection.injectRaw(ClassInjector.java:284) at net.bytebuddy.dynamic.loading.ClassInjector$AbstractBase.inject(ClassInjector.java:118) at net.bytebuddy.dynamic.loading.ClassLoadingStrategy$Default$InjectionDispatcher.load(ClassLoadingStrategy.java:241) at net.bytebuddy.dynamic.TypeResolutionStrategy$Passive.initialize(TypeResolutionStrategy.java:101) at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:6317) at com.org.common.util.AdapterFactory.buildAdapter(AdapterFactory.java:118) at com.org.oracle.core.jdbc.OracleConnectionFactory.createOCIJDBCConnectionInNewJvm(OracleConnectionFactory.java:141) ... 59 common frames omitted Caused by: java.lang.OutOfMemoryError: Metaspace
Environment details: Java: tried with 2 versions and java options are "-Xms256m -Xmx2G -XX:MaxMetaspaceSize=256m -XX:ParallelGCThreads=8" openjdk version "1.8.0_242" OpenJDK Runtime Environment (build 1.8.0_242-b08) OpenJDK 64-Bit Server VM (build 25.242-b08, mixed mode)
openjdk version "11.0.19" 2023-04-18 LTS OpenJDK Runtime Environment (Red_Hat-11.0.19.0.7-1.el7_9) (build 11.0.19+7-LTS) OpenJDK 64-Bit Server VM (Red_Hat-11.0.19.0.7-1.el7_9) (build 11.0.19+7-LTS, mixed mode, sharing)
OS: RHEL 7.8
ByteBuddy version: 1.14.1
Thanks, Vinayak
@raphw
Is there a way to implement caching for the below solution? Can you provide us a way to cache the classes so that the metaspace doesn't get filled up?
T adapter = new ByteBuddy() .subclass(clazz) .method((ElementMatchers.not(ElementMatchers.isNative()))) .intercept(MethodDelegation.to(ADAPTER_METHOD_INTERCEPTOR)) .make().load(AdapterFactory.class.getClassLoader(), ClassLoadingStrategy.Default.INJECTION.with(AdapterFactory.class.getProtectionDomain()) .getLoaded().newInstance(); adapter.setAdaptee(adaptee);
You should use a TypeCache and not create new classes for each proxy.