在Byte Buddy中缓存生成的类?

时间:2014-05-19 07:40:15

标签: java code-generation bytecode proxy-classes byte-buddy

我一直在使用CGLIB的Enhancer,但考虑切换到Byte Buddy。它是非常基本的东西,可以根据需要代理多达数百个数据访问接口。

Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(...);
enhancer.setSuperclass(...);
enhancer.setInterfaces(...);
enhancer.setCallback(...);
enhancer.create();

CGLIB正在缓存生成的类型以提高性能。 Byte Buddy的推荐方法是什么?我想避免任何PermGen问题。

1 个答案:

答案 0 :(得分:3)

更新:从版本1.6开始,Byte Buddy提供TypeCache类,它使用软引用或弱引用作为使用自定义键编写缓存的蓝图。此缓存包含一个回调方法findOrInsert,允许按需创建类型。

对于Byte Buddy,您应该编写自己的缓存,因为您最了解的是:

  • 什么标准对缓存有意义 - 也许您需要两个具有不同身份的类似类? Byte Buddy不应该为你做出决定。
  • 维护缓存需要保留哪些信息? Cglib需要跟踪所有可以防止垃圾收集的输入信息,即使是用短期"班级装载机。

cglib将静态字段的内部缓存与同步映射保持在一起,这带来了一些严重的限制。使用此缓存,在使用缓存时查询任何 Enhancer实例时,您决定使用类的身份的时间不长。此外,静态字段需要跟踪创建类的参数,例如输入回调的标识,这可能非常繁重。 (事实上​​,它本身会造成内存泄漏。)

Byte Buddy希望成为生成任何Java类的API,而不仅仅是创建代理。因此,您应该最清楚哪种缓存是有意义的。考虑您只想代理实例的场景。写一个简单的外观,如:

class MyProxyGenerator {
  static Map<Class<?>, Class<?>> proxies = new HashMap<>();
  public Class<?> makeProxy(Class<?> type) {
    if(proxies.contains(type)) {
      return proxies.get(type);
    } else {
      Class<?> proxy = doMakeProxy(type);
      proxies.put(type, proxy);
      return proxy;
    }
  }
  private Class<?> doMakeProxy(Class<?> type) {
    // use Byte Buddy here.
  }
}

这里的优点是您只需要将输入类作为缓存引用进行跟踪,如果应用程序是单线程的,则可以避免同步。此外,如果更适合您的用例,您可以将缓存定义为非静态缓存。甚至更好:您可以使用真正的缓存实现。这样,每个图书馆都可以做到最好。 Byte Buddy可以创建类,缓存也可以缓存。

为了完全披露,我是Byte Buddy的作者,我在使用cglib和javassist一段时间后决定采用这种方法。我在缓存方面遇到了一些问题,这就是为什么我决定不向Byte Buddy提供这样的缓存。我认为它更像是由JDK代理隐式缓存引入的约定,但我不相信这些缓存通常是出于上述原因的好主意。