1

我有一个回归测试,我试图计算数组实例化的数量。具体来说,我有兴趣计算new int[..]被调用的次数。我已经整理了以下基于 ByteBuddy 建议的工具,我认为它们应该可以捕获所有构造函数,但这不适用于数组(任何类型的):

public class InstanceCounter {
  public static void load() {
    ClassInjector.UsingUnsafe.ofBootLoader().injectRaw(Collections.singletonMap("foo.bar.BootstrapState", readBytes(InstanceCounter.class.getClassLoader().getResource("foo/bar/BootstrapState.class"))));
    new AgentBuilder.Default()
      .ignore(none())
      .disableClassFormatChanges()
      .with(RedefinitionStrategy.RETRANSFORMATION)
      .with(InitializationStrategy.NoOp.INSTANCE)
      .with(TypeStrategy.Default.REDEFINE)
    .type(any())
      .transform(new Transformer() {
        @Override
        public Builder<?> transform(final Builder<?> builder, final TypeDescription typeDescription, final ClassLoader classLoader, final JavaModule module) {
          return builder.visit(Advice.to(InstanceCounter.class).on(any()));
        }})
    .installOn(ByteBuddyAgent.install());
  }

  @Advice.OnMethodEnter
  public static void enter(final @Advice.Origin Class<?> origin) {
    if (!BootstrapState.trace || BootstrapState.lock)
      return;

    BootstrapState.lock = true;
    System.out.println(">>> " + origin);
    BootstrapState.lock = false;
  }
}

public class InstanceCounterTest {
  @Test
  public void test() {
    InstanceCounter.load();
    System.out.println("Creating array...");
    BootstrapState.trace = true;
    final Object[] y = new Object[3];
    BootstrapState.trace = false;
    System.out.println("Printing array: " + y);
  }
}

当我运行这个测试时,我得到以下结果:

Creating array...
Printing array: [Ljava.lang.Object;@7f5eae0f

如果我更改final Object[] y = new Object[3]new Object(),则输出为:

Creating array...
>>> class java.lang.Object
Printing array: [Ljava.lang.Object;@68562ad5

我已经使建议工具尽可能开放(至少在我认为可以做到的范围内开放),但这仍然没有检测数组对象的构造。

甚至可以检测数组对象的构造吗?

2020-07-28 更新

根据原始问题的上下文,提供的答案表示 ByteBuddy 不直接支持new int[]. 但是,对于那些对替代解决方案感兴趣的人,我能够使用Byteman实现这种检测。

4

3 回答 3

1

正如所指出的,您不能更改数组类,因为它们没有在类文件中表示。相反,您需要拦截它们的创建,这需要一些字节码解析。

Byte Buddy公开了 ASM,它使用它自己来完成这种工作,但它比拦截构造函数要手动得多。

于 2020-07-12T21:43:18.000 回答
1

数组没有构造函数;它们是使用newarray(for primitives) 或anewarray(for reference types)创建的(multianewarray如果您想获得创意,也可以使用)。

如果要修改数组创建,则必须找到某种方法来检测这些字节码指令。

于 2020-07-11T06:42:37.840 回答
1

常规对象构造的检测涉及将调用注入到相关类的构造函数中。这是可行的,因为每个类都至少声明了一个构造函数,并且构造函数总是在对象创建期间被调用。

相比之下,数组没有构造函数,因此无法注入调用。这就解释了为什么您对所有构造函数的检测都没有拾取数组创建。

假设,仪器数组创建的另一种方法是找到使用 3 个“newarray”字节码的所有位置,并在它们之前进行检测调用。

于 2020-07-11T06:43:58.533 回答