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

Potential Memory Leak because of move_and_slide() against Each Collidable Bodies #84036

Open
Lazy-Rabbit-2001 opened this issue Oct 27, 2023 · 8 comments

Comments

@Lazy-Rabbit-2001
Copy link
Contributor

Lazy-Rabbit-2001 commented Oct 27, 2023

Godot version

4.2 beta 3 ~ 4.3 dev 6

System information

Windows 11, GLES3

Issue description

This is an extended issue from #57073 opened by @BeayemX.
At first I didn't realize that get_slide_collision() could be a memory leaker until I found something abnormal yesterday when I test a GDExtension, in which I used this call to get the last slide collision and emit some signals with this object, but when the debug began and I tested it for seconds, I found the "Object" amount in profiler was increasing. And then I searched around the issues tab and finally found that issue at the beginning of this report.
But things is always coming with dark clouds. When I returned to remove this call, a weird thing happened: the amount of object was still increasing after the removal of using the method! To research more, I started figuring out what happened with this phenomenon and eventually found this dark cloud:
I opened a new project with only a CharacterBody2D using template moving code, and a TileMaps with tiles. Every time the move_and_slide() is called and the collision between the body calling the method and another one that the body haven't collided with (if it's a TileMap, each piece of tile is considered as an individual collision body), the amount of collision increased.

Could this be a high-priority problem?

Yes, and the reason lays on two aspects:

  1. Most of developers are not aware of this issue, which made them call this method more, increasing the risk to lead to memory leak
  2. Quoted from the issue in the first line:

Looking to backtraces on gdb, ret_opaque is a pointer to a Ref that references count increases in method->ptrcall(base_obj, argptrs, ret_opaque); but isn't unreferenced therefore. The references count also increases in VariantInternal::object_assign(ret, *ret_opaque);, but is unferenced at the end of the gdscript scope:

If this is true, then this is a disastrous potential glitch with the process of RefCounted.

Why should this be a independent and new issue rather than response in that issue?

This has been not only an issue related to get_slide_collision(), but even one to move_and_slide() or even to RefCounted.

Steps to reproduce

Make a CharacterBody2D move with move_and_slide() and collide with any collision bodies.

Minimal reproduction project

Issue.zip
(No GDExtensions)

@Lazy-Rabbit-2001 Lazy-Rabbit-2001 changed the title [High Priority Probably] Potential memory leak because of move_and_slide() against Each Collidable Bodies [High Priority Probably] Potential Memory Leak because of move_and_slide() against Each Collidable Bodies Oct 27, 2023
@Lazy-Rabbit-2001 Lazy-Rabbit-2001 changed the title [High Priority Probably] Potential Memory Leak because of move_and_slide() against Each Collidable Bodies Potential Memory Leak because of move_and_slide() against Each Collidable Bodies Oct 27, 2023
@akien-mga
Copy link
Member

Please provide a reproduction project. The exact setup you're using is necessary to reproduce the issue. It sounds like you're using GDExtension which isn't mentioned at all in the steps to reproduce, and is far from trivial to setup.

@Torguen
Copy link

Torguen commented Oct 27, 2023

I can't see the video, nothing is shown even if I press the play button.

@Lazy-Rabbit-2001
Copy link
Contributor Author

Please provide a reproduction project. The exact setup you're using is necessary to reproduce the issue. It sounds like you're using GDExtension which isn't mentioned at all in the steps to reproduce, and is far from trivial to setup.

I attached it now

@akien-mga
Copy link
Member

Thanks, I can reproduce the issue that the Object count seems to keep increasing.

@kleonc
Copy link
Member

kleonc commented Oct 27, 2023

This is an extended issue from #83339 opened by @BeayemX.

@Lazy-Rabbit-2001 Wrong issue linked?


I opened a new project with only a CharacterBody2D using template moving code, and a TileMaps with tiles. Every time the move_and_slide() is called and the collision between the body calling the method and another one that the body haven't collided with (if it's a TileMap, each piece of tile is considered as an individual collision body), the amount of collision increased.

The increasing Object count seems to be GodotPhysicsDirectBodyState2Ds being created whenever GodotBody2D::get_direct_state() is called for the first time for the given body. So it's one time thing for each body, it's not like 2 Objects will be created when different CharacterBody2Ds will collide with the same tile etc.

PhysicsDirectBodyState2D *bs = PhysicsServer2D::get_singleton()->body_get_direct_state(platform_rid);

return body->get_direct_state();

GodotPhysicsDirectBodyState2D *GodotBody2D::get_direct_state() {
if (!direct_state) {
direct_state = memnew(GodotPhysicsDirectBodyState2D);
direct_state->body = this;
}
return direct_state;
}

Not sure if that's a bug, such created GodotPhysicsDirectBodyState2D is cleared in the GodotBody2D's destructor:

GodotBody2D::~GodotBody2D() {
if (fi_callback_data) {
memdelete(fi_callback_data);
}
if (direct_state) {
memdelete(direct_state);
}
}

@Lazy-Rabbit-2001
Copy link
Contributor Author

Lazy-Rabbit-2001 commented Oct 27, 2023

This is an extended issue from #83339 opened by @BeayemX.

@Lazy-Rabbit-2001 Wrong issue linked?


I opened a new project with only a CharacterBody2D using template moving code, and a TileMaps with tiles. Every time the move_and_slide() is called and the collision between the body calling the method and another one that the body haven't collided with (if it's a TileMap, each piece of tile is considered as an individual collision body), the amount of collision increased.

The increasing Object count seems to be GodotPhysicsDirectBodyState2Ds being created whenever GodotBody2D::get_direct_state() is called for the first time for the given body. So it's one time thing for each body, it's not like 2 Objects will be created when different CharacterBody2Ds will collide with the same tile etc.

PhysicsDirectBodyState2D *bs = PhysicsServer2D::get_singleton()->body_get_direct_state(platform_rid);

return body->get_direct_state();

GodotPhysicsDirectBodyState2D *GodotBody2D::get_direct_state() {
if (!direct_state) {
direct_state = memnew(GodotPhysicsDirectBodyState2D);
direct_state->body = this;
}
return direct_state;
}

Not sure if that's a bug, such created GodotPhysicsDirectBodyState2D is cleared in the GodotBody2D's destructor:

GodotBody2D::~GodotBody2D() {
if (fi_callback_data) {
memdelete(fi_callback_data);
}
if (direct_state) {
memdelete(direct_state);
}
}

sorry, the link does be wrong and I fixed it just now
as for the clearness, there seems to be something that prevents the state from being destructed...
(sorry, I haven't learnt well how these codes work and this is just a simple track on my point of view

@Lazy-Rabbit-2001
Copy link
Contributor Author

Lazy-Rabbit-2001 commented Nov 18, 2023

Not sure if that's a bug, such created GodotPhysicsDirectBodyState2D is cleared in the GodotBody2D's destructor:

GodotBody2D::~GodotBody2D() {
if (fi_callback_data) {
memdelete(fi_callback_data);
}
if (direct_state) {
memdelete(direct_state);
}
}

I mentioned an issue that the direct state can get deleted only when the body is removed from the memory, but what if the body is running?
I think there seems to lack a way to remove previous direct state to ensure the RAM is guarded from leaking.
(Due to my busy life I haven't enough time to look through the relative cpp files to certify if the bug does happen because of the lack of removing stored direct state in the runtime of the body, so this is just my own PoV on this problem)

@Lazy-Rabbit-2001
Copy link
Contributor Author

Lazy-Rabbit-2001 commented Apr 14, 2024

Seems that the pr #90668 may fix this issue, as I saw that the pr switched pointer to ObjectID that may seem to solve the problem of making RefCounted being undeletable.
I know almost nothing about how the low-level codes work, but this pr gave me that image.

Once the pr gets merged and another test for the bug, which will be conducted in the version where the feature of that pr gets implemented, does not fail freeing the KinematicCollisions, this topic will get closed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants