sofastack / sofa-rpc

SOFARPC is a high-performance, high-extensibility, production-level Java RPC framework.
https://www.sofastack.tech/sofa-rpc/docs/Home
Apache License 2.0
3.81k stars 1.16k forks source link

Frequently creating Fury instances, leading to FGC #1424

Open Lo1nt opened 6 days ago

Lo1nt commented 6 days ago

Frequently creating Fury instance lead to FGC

During encode and decode, fury.setCassloader and fury.clearClassloader are called before and after the actual serialization part; threadLocalFury are applied as default, arising following problems:

ThreadLocalFury introduce at least one fury instance for one thread

Now SOFARPC use ThreadLocalFury, will maintain at least one Fury instance for each thread. Which already introduce large amount of Fury instances while the amount of threads getting larger. (And notice that for each fury instance, its own class resolver, which usually become large object in heap, will be created.) Within this scope, ThreadLocalFury might not be that appropriate for the situation.

New Fury instance created after clearClassLoader is called

The clearClassLoader will clear bindingThreadLocal.furyInstance and give birth to instantiating of new Fury instance. This mechanism aggravate the abundance of fury instances in heap and increasing the likelihood of full GC.

Expected behavior

Growth rate of fury instances should be controllable.

Actual behavior

In our case, 20000 tps introduce 4581 fury instances. (For same situation, hessian could bears for 90 k tps)

Steps to reproduce

Simple benchmark on fury-sofarpc call.

Minimal yet complete reproducer code (or GitHub URL to code)

{
        ServerConfig serverConfig;
        ProviderConfig<UserService> providerConfig;
        serverConfig = new ServerConfig()
                .setProtocol("bolt")
                .setPort(Integer.parseInt(port))
                .setCoreThreads(1000)
                .setMaxThreads(2000);

        providerConfig = new ProviderConfig<UserService>()
                .setInterfaceId(UserService.class.getName())
                .setRef(new UserServiceServerImpl())
                .setServer(serverConfig);

        providerConfig.export();
}
{
        ConsumerConfig<UserService> consumerConfig = new ConsumerConfig<UserService>()
                .setRepeatedReferLimit(10)
                .setInterfaceId(UserService.class.getName())
                .setProtocol("bolt")
                .setParameter("tr.protocol.default", "bolt")
                .setDirectUrl("bolt://127.0.0.1:" + 12200).setConnectionNum(2000).setTimeout(10000);

        consumerConfig.setSerialization("fury2");

        UserService userService = consumerConfig.refer();

        userService.existUser("dasd");
}

Environment