-
Notifications
You must be signed in to change notification settings - Fork 9
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 when deleting root commands #75
Comments
That's minecraft keeping a reference in the brigadier dispatcher. You can't delete that. It's going to be deleted on reload or restart. |
@rgnter Why isn't that deletable? Do you mean through API? |
When plugins are enabled, bukkit commands are merged with vanilla commands, and stored away in command dispatcher. This dispatcher is not available through the API. The wrapped command references bukkit command, which references cloud command. |
My question is why can't we just access the map with reflection and remove the command? |
Because using reflection is inefficient, burdensome(from maintenance point of view) and slow. Let them keep the reference, it's not that much trouble for us. |
Okay that's understandable but we should at least break the chain that links the command class (Where you define the command annotations) to the BukkitCommand, bc if the chain hasn't been broken that class can't be garbage collected and that could cause a big memory leak depending on how someone has structured their code. |
I like how you think, but we can't. It's impossible
And honestly, the "leak" is trivial. Nobody really removes nodes. And if, the memory footprint wouldn't be that huge to call it a "leak" (couple of hundreds bytes in HEAP). It is irresponsible, but we can't do much about it, unless we get direct access to the brigadier API. |
What I meant by breaking the chain was setting this field to null |
So, we can't make it null, because the node doesn't know when it's removed. And if it knew it's removed, how can we be sure that the node is not going to be added elsewhere. |
The leak could be big depending on how your code is structured. For example, you just need to have a cache that stores some data or a reference to an instance that holds and manages a cache. I think that this problem shouldn't be overlooked or at least should be mentioned in the doc of the deleteRootCommand bc
That's true this means that we should provide a way to "invalidate" the MethodCommandExecutionHandler. |
I see your concern, but hacking our way through the API just to fix one small issue for Bukkit is kinda-sorta bad. The issue only occurs when deleting a node which is based on annotations (API for node removal is also experimental). Lesson learned: decouple your logic and data. We probably can't do much other than document it. |
I don't think that hacking is the only way to fix this. There are other ways as I stated above |
By hacking I meant introducing new experimental API to release the method handle. 😀 There's really no way for us to tell when to safely release the method handle. You're right that this situation is bad and irresponsible of us to ignore, but when user of the library - in this case Bukkit, is holding a reference we can't so much about it. Bukkit never intended for commands to be removed. Imagine case where you provide a annotation based command, and another internal mechanism takes the reference to that command (not just bukkit) and you decide to release the method handle just because you removed the root node. solution: Disable the removal API for nodes on the Bukkit platform. Until direct interoperability with Brigadier is possible. |
Although I might have a simple solution to this. Once I'm available I'll try to craft something. |
I was thinking of wrapping the MethodHandle inside a WeakReference, this way everyone is made conscious that the reference may become null at any given PIT and thus should handle the case. |
Wrapping the methodHandle directly would cause the GC to remove the command almost immediately. As there would be no strong referent. |
I meant on the getter, internally it would still be used as a normal reference. |
Btw, I've just seen that the CommandMethodContext has references to the instance as well |
Hello, has this been fixed since I just got to this. My proposed fix is in the BukkitRegistrationHandler, it uses the player#syncCommands to sync commands to players, but that doesn't update dispatcher, to update the dispatcher you can use CraftServer#syncCommands() method that will update the dispatcher and players. Need some reflection, but that's how I got it to reload properly. Also noticed that not all aliases are being removed, only the namespaced ones from the knownCommandMap |
When you delete a root command by using #deleteRootCommand some references to it are kept.
I'm using cloud-paper and cloud-annotations 1.8.3 on a 1.19.3 server
The text was updated successfully, but these errors were encountered: