Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memory Leak #1162

Open
brianfromoregon opened this issue Sep 18, 2023 · 3 comments
Open

Memory Leak #1162

brianfromoregon opened this issue Sep 18, 2023 · 3 comments
Labels
for user attention Submitter needs to provide additional details

Comments

@brianfromoregon
Copy link

To Reproduce:

Run this code in a loop on Java17 with -Xmx40m. For me it only goes four iterations and hits OOM on the fifth.

      GatewayDiscordClient gateway = DiscordClient.create(discordBotToken)
          .gateway()
          .setEnabledIntents(IntentSet.of(Intent.GUILD_MEMBERS))
          .login()
          .block();
      Map<Long, String> usernamesById = gateway.getGuildMembers(Snowflake.of(guildId)).toStream()
          .collect(Collectors.toMap(m -> m.getId().asLong(), Member::getUsername));
      gateway.logout();
      this.usernamesById.putAll(usernamesById);
      logger.info("Guild member fetch complete: {} members", usernamesById.size());

Expected Behavior:

I expected logout() to release any resources, and when my GatewayDiscordClient instance gets GC'd then its fully cleaned up.

Actual Behavior:

There are statically retained objects created each time running that code.

Version:

3.2.3

Other:

My server app has been dying with OOME since incorporating discord4j. I use version 3.2.3.

Eclipse MAT showed this in the heap dump:

image

These ObjectMapper instances are being created each time thru the loop.

image

This shows the paths to GC roots for one of the instances:

image

@quanticc
Copy link
Member

gateway.logout() needs to be subscribed with subscribe or block in order to disconnect and begin to release resources. Can you check if the issue persists after adding that?

Beyond that, GatewayDiscordClient is an expensive resource, so if you need to reuse the token there are more efficient ways to do this. ObjectMapper (JacksonResources in D4J) can be reused in case you need to do this under multiple bot tokens.

@quanticc quanticc added the for user attention Submitter needs to provide additional details label Sep 20, 2023
@brianfromoregon
Copy link
Author

Thanks. Adding .block() after .disconnect() fixes the issue (my 40m Xmx test did not crash after 25 iterations, so i stopped it)

Given it's expensive, I can try keeping my GatewayDiscordClient instance long lived and never logout. My app could run for months at a time, do you foresee issues with that approach?

@quanticc
Copy link
Member

No issues, that's the recommended approach for a general use case. You would only have to spin multiple GatewayDiscordClients if you need to use different bot tokens.

However if you only need the guild members, you don't require a real-time bot connection (Gateway). Could just use the REST client like this:

DiscordClient client = DiscordClient.create(token);
Map<Long, String> usernamesById = client.getGuildById(Snowflake.of(guildId)).getMembers()
        .collect(Collectors.toMap(m -> m.user().id().asLong(), m -> m.user().username()))
        .block();

Again reusing DiscordClient if you don't have multiple bot tokens.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
for user attention Submitter needs to provide additional details
Projects
None yet
Development

No branches or pull requests

2 participants