-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Can we / shouldn't we use Dart VM (GC) to auto manage non-Dart objects? #54233
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
Comments
If you are only dealing with a few small objects that do not hold indirect references to larger objects, it's ok to rely on the gc. If you are holding anything of significant size or that holds significant/limited native resources, you should not rely on the gc. |
@dnfield Thank you very much! I will add this to doc telling the users (properly quoting the source, surely). |
The main difference between holding on to large Dart objects and holding on to large foreign objects, is that if the Dart heap is running out of space, it will trigger a GC to clean itself up. If the foreign heap is running out of space, it won't be able to trigger a Dart GC to free up the remote pointers into its heap. So, unless Dart provides a way for the foreign heap's GC to trigger a Dart GC (and it knows how to use it), the memory might not be freed even if it's GC'able and needed. |
@lrhn Thank you for the reply! My case (flutter_rust_bridge) is Rust, which does not have a GC or a heap (unlike Java). Thus, if I understand correctly, this means, holding on to large Dart objects and holding on to large foreign objects do not have big differences? |
The main difference is that the garbage collector will eventually (possibly after a long time) invoke the finalizer that then frees up the native resource. So reclaiming the native resource is delayed - compared to an eager, manual disposal. As was stated earlier, it depends what native resource you're managing: Imagine your native resource is an opened file (i.e. we have a file descriptor to close). Imagine now you have a loop that opens thousands of files and rely on the GC to eventually invoke a finalizer on them (which will trigger closing of the file descriptor). Now the actual file objects in the Dart heap are very small, so it's very likely that the GC won't run for a while and one runs into the |
@mkustermann Thank you for the reply! I will also add these (with references) to docs for the users to understand what to do. |
Would it make a positive difference here if we tell the |
@julemand101 From my naive understanding, if the externalSize is also small (since in rust, an opened file may be just some kind of descriptor), then it may not be super helpful. |
@fzyzcjy Ah yeah that is a fair assumption. I thought your Rust objects had a large size but yeah, if both Dart and native side are small, that would make GC unaware of the benefit of freeing these resources. |
Btw I cannot make any assumptions about "my" rust objects, since https://github.com/fzyzcjy/flutter_rust_bridge is a library, so the dev who uses it can do anything ;) |
This ended up being an anti-pattern for Flutter. Telling the VM about the external size made GCs trigger more often but not always for the reasons you'd want - the VM would see the heap was super big, run a GC, and collect no memory because the super big external object was still "alive". It would then see another really large allocation come in (and now the original large allocation was unreachable as a dart object), schedule a GC, but oops! the process ran out of memory and got killed by the LMK before the GC could run because it tried to allocate too much memory before freeing the old stuff. |
This was particularly bad for images, which tended to be large, but also extended to other objects that end up with a shared pointer to the image (or to another shared pointer that has a shared pointer to the image) natively, like |
We also had spots in the code base where we literally were just "guessing" how big the native allocation would be because the library we were using didn't provide it, and there would be comments like |
@dnfield Thank you for the details! |
I presume the question has been answered so closing issue. |
Hi thanks for the Dart language, the Flutter framework, and the ecosystem! I wonder, in 2023, should or should not we use Dart VM (GC) to manage non-Dart objects? It seems that, if I understand correctly, there are two conflicting view of points as follows. Please correct me if I am wrong!
Positive
The https://github.com/dart-lang/native/tree/main/pkgs/jnigen seems to manage Java objects automatically, and does not require a manual call of
dispose
. It uses the native finalizer to auto dispose the object. In the example, it seems thatJObject
s are created, and no manualdispose
is called (but rely on Dart VM's GC to auto clear it).P.S. https://pyo3.rs/ (the binding between Rust and Python) seems to also let Python heap to manage the Rust objects as well, if I understand correctly. But that is a different story, since people do not use Python for mobile apps (which has quite limited memory).
Negative
A long time ago, there were some discussions, and @dnfield said in the comments:
(there are some context to read around that comment as well)
Question
Therefore, I wonder whether in 2023 we can / should do that? I personally hope we can do it, because it can make https://github.com/fzyzcjy/flutter_rust_bridge v2 much easier to use when it comes to arbitrary non-serializable types - users do not need to remember calling
dispose
here and there (which is quite annoying).The text was updated successfully, but these errors were encountered: