raphw / byte-buddy

Runtime code generation for the Java virtual machine.
https://bytebuddy.net
Apache License 2.0
6.29k stars 807 forks source link

install transformer takes too long time #1644

Closed dre4merp closed 1 month ago

dre4merp commented 6 months ago

My purpose is to hook some functions to implement monitoring. There are about 40 hook points. I am using the Advice method. When I do the installation of the hitch points, it takes a long time. Through debugging I found that the following function takes a long time. The main reason should be this for-for loop. There are about 27k classes in types. Moreover, I executed the installOn method about 40 times, which took too much time.

Is there a good way to optimize ?

Here is part of code.


    private final AgentBuilder commonAgentBuilder = new AgentBuilder.Default()
        .disableClassFormatChanges()
        .ignore(ElementMatchers.none())
        .ignore(ElementMatchers.nameStartsWith(ignorePackagesArray[0]))
        .ignore(ElementMatchers.nameStartsWith(ignorePackagesArray[1]))
        .with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)
        .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
        .with(AgentBuilder.TypeStrategy.Default.REDEFINE);

        for (AdviceInfo advice : adviceInfo) {

            AgentBuilder.Transformer transformer;
            ResettableClassFileTransformer resetAgentBuilder;

                transformer = new Transformer() {
                    @Override
                    public Builder<?> transform(Builder<?> builder, TypeDescription typeDescription,
                        ClassLoader classLoader, JavaModule module, ProtectionDomain protectionDomain) {
                        return builder
                            .visit(net.bytebuddy.asm.Advice.to(MethodInterceptors.class)
                                .on(methodMatcher));
                    }
                };
                resetAgentBuilder = commonAgentBuilder
                    .type(classMatcher)
                    .transform(transformer)
                    .installOn(instrumentation);
    }

Here is the function which takes long time. net.bytebuddy.agent.builder.AgentBuilder.RedefinitionStrategy#apply

            for (Iterable<Class<?>> types : redefinitionDiscoveryStrategy.resolve(instrumentation)) {
                RedefinitionStrategy.Collector collector = make(...);
                for (Class<?> type : types) {   // types has 27k classes
                    if (...) {
                        continue;
                    }
                    collector.consider(type, DISPATCHER.isModifiableClass(instrumentation, type) || ClassFileVersion.ofThisVm(ClassFileVersion.JAVA_V5).isAtMost(ClassFileVersion.JAVA_V5));
                }
                batch = collector.apply(instrumentation, redefinitionBatchAllocator, redefinitionListener, batch);
            }

And the same to

        for (Advice advice : adviceMap.values()) {
            advice.getResetTransformer().reset(instrumentation, AgentBuilder.RedefinitionStrategy.RETRANSFORMATION);
        }
raphw commented 6 months ago

You should concatenate the agent builder and only install it a single time after the loop. The transformations are additive by default.

dre4merp commented 6 months ago

Do u mean code like this?

        for (AdviceInfo advice : adviceInfo) {
                transformer = new Transformer() {...};
                commonAgentBuilder
                    .type(classMatcher)
                    .transform(transformer)
        }
        resetAgentBuilder = commonAgentBuilder.installOn(instrumentation);

I tried it, it works but not obvious. Mainly I want to minimize the impact of too many classes (types). What should I do? Would it be more efficient to have the builder ignore more classes?

raphw commented 6 months ago

The fewer classes are transformed, the better. But retransforming is in itself expensive, so this reduces iterations by a lot.