-
-
Notifications
You must be signed in to change notification settings - Fork 582
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
Accessing Dictionary from inside of Array results in a memory leak #1240
Comments
Thanks for the report! Can you explain how you're detecting the memory leak? |
Dictionary
from inside of Array
results in a memory leak
Dictionary
from inside of Array
results in a memory leak
I've also hit this memory leak accessing an array of arrays. I narrowed it down by commenting and retrying >,< /* Check if test input conflicts with other entries within a given column of the playfield */
bool GridCalc::valid_in_grid_col(const Array &grid, int col, int row, int row_count, int test)
{
// std::vector<int> grid_col = (std::vector<int>)grid[col];
const Array &grid_col = (const Array &)grid[col];
godot::UtilityFunctions::print(vformat("GridCalc::valid_in_grid_col Checking grid_col vector: %d", (int)grid_col[0]));
for (int check = 0; check < row_count; check++)
{
if (check == row)
{
continue;
}
// if ((int)grid_col[check] == test)
// {
// return false;
// }
}
return true;
}
Based on my testing (left the commented code in for reference), all it takes is initializing the Here is the end of my valgrind log (full log is >100mb, I can pastebin it if desired):
* NB: I ran valgrind with 4.1.3, but 4.2 hasn't alleviated the issue. |
I tried the MRP in the original description, and while Valgrind isn't reporting anything useful to me, I do see the memory rising constantly when this code is included:
... but the memory remains stable if I comment that out. So, I'm seeing the leak too! |
System Info:
I have experienced a similar issue that I believe shares the same root cause. From looking at the allocation call stack of my example and the original example, it seems that the leak is caused by casting from The following code leaks, but only in C++ GDExtension. If I compile this code as a part of the main Godot source, it does not leak: Array original_array;
for (int i = 0; i < 1000; i++) {
Variant my_variant = original_array;
Array my_array = my_variant; // This line causes memory to leak
} The leak also happens if I used Visual Studio Allocation Call StackHere is the Visual Studio Allocation Call Stack (as seen using a local MSVC build of Godot 4.2.1): Comparison to Core EngineI see that GDExtension uses this sort of cast operator in Variant::operator Array() const {
Array result;
to_type_constructor[ARRAY](result._native_ptr(), _native_ptr());
return result;
} ...which is introduces additional code paths compared to the Variant::operator Array() const {
if (type == ARRAY) {
return *reinterpret_cast<const Array *>(_data._mem);
} else {
return _convert_array_from_variant<Array>(*this);
}
} My example of the leak does not trigger in the core engine because the ...But in GDExtension, although it seems like it does this check, it still eventually ends up at |
It's possible this leak has some similarities to a chain of leaks from way back when ( #490 #356 #355). Is it possible some of these sorts of issues were reintroduced with e4ed489 or some other major change? (I'm just trying to do some git log detective work here 🕵️)
I think this is a good time for me to learn about rvalues, Leak DetailsHere's a very boiled down version of the leak: Array original_array;
Variant my_variant = original_array;
Array my_array = my_variant; // This line causes memory to leak The cast that happens during that assignment creates an additional
Eventually another call to GDExtension's It seems clear that ref "B" in the above scenario is never unreferencing and causing the leak... |
Alright, I understand what's happening now and I might have a fix(?) Here are the details: I am going to use the The leak happens when calling the Dictionary original_dictionary;
Variant my_variant = original_dictionary;
Dictionary my_dictionary = my_variant; // The cast operator of GDExtenion's Variant causes a leak When this cast happens, a temporary Variant::operator Dictionary() const {
Dictionary result;
to_type_constructor[DICTIONARY](result._native_ptr(), _native_ptr());
return result;
} The Dictionary::Dictionary(const Dictionary &p_from) {
_p = nullptr;
_ref(p_from);
} If calling Variant::operator Dictionary() const {
Dictionary result;
result.~Dictionary(); // Adding this line fixes the leak
to_type_constructor[DICTIONARY](result._native_ptr(), _native_ptr());
return result;
} I have tested it and found that it successfully fixes the leak that was demonstrated in the original issue as well as the leak that I was experiencing. But, this seems sub-optimal to be constructing a |
Thanks @dsnopek! Unfortunately it looks like this method leaks (it leaves an |
The draft PR only did |
Oh, sorry, my mistake on my comment — I adapted your PR code to Your proposed approach seems to work similar to some code I was fiddling with using uninitialized memory, but there seems to be an extra constructor call that I don’t quite understand yet. EDIT: I've moved discussion of a fix to the PR draft #1378. |
I've tried the original post's minimal reproduction project with the 4.2.2 godot-cpp release and: Godot v4.2.2.stable - Windows 10.0.22631 - Vulkan (Forward+) - dedicated NVIDIA GeForce GTX 980 Ti (NVIDIA; 31.0.15.3598) - 13th Gen Intel(R) Core(TM) i7-13700K (24 Threads) And it is no longer leaking: I also tried with the old versions: Godot v4.1.1.stable - Windows 10.0.22631 - Vulkan (Forward+) - dedicated NVIDIA GeForce GTX 980 Ti (NVIDIA; 31.0.15.3598) - 13th Gen Intel(R) Core(TM) i7-13700K (24 Threads) This issue can now be closed! :) |
Thank you for updating! |
Godot version
4.1.1
godot-cpp version
4.1
System information
Fedora Silverblue 38
Issue description
This function results in a memory leak.
Recreating this function line by line in GDScript doesn't result in memory leak.
I've tried to return Dictionary as is:
Which works fine.
Callable and
callv()
are affected too.Steps to reproduce
N/A
Minimal reproduction project
gdextension-memory-leak-main.zip
You can also view it here: https://github.com/Lasuch69/gdextension-memory-leak
The text was updated successfully, but these errors were encountered: