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

DartNative's memory management strategy is strongly discouraged by the official Dart team :( #88

Open
fzyzcjy opened this issue Jan 5, 2022 · 9 comments
Assignees

Comments

@fzyzcjy
Copy link

fzyzcjy commented Jan 5, 2022

Hi, I was implementing the fully automatic memory management between Dart/Flutter and Rust (see fzyzcjy/flutter_rust_bridge#243), so I asked some questions to the Dart team, and I find the following reply also applies to your case (http://yulingtianxia.com/blog/2020/08/22/DartNative-Automatic-Memory-Management/):

I would strongly discourage anyone from using GC to manage non-Dart objects. If you want to manage native object lifetimes, have an explicit method like close or dispose to release native resources. GC might not ever run, or run too late.

The VM can be aware of external object size, but GC pressure comes from new allocations. It's really not that hard to get into a scenario where you have an external object that takes up very close to a ceiling of memory you want to use, but not enough to trigger a GC. Then, new allocations happen quickly and kick off a GC, but you then run out of memory (or file handles, or some other native limited resource) before the GC can finish.

Flutter had this problem with images for example - we were relying on the GC to clean them up, which works pretty often but does not work so well when you try to load larger images in memory constrained environments. The more we tried to make the GC clean this up for us, the worse it got - the GC sometimes couldn't work fast enough (so new images could get allocated before the GC-able ones got cleaned up, leading to OOMs), and we also were artificially running the GC too often (Because it saw these huge objects it thought it was responsible for cleaning up). So now we explicitly and eagerly dispose images/graphics resources, and you can't get into that race anymore (and we don't need as many GCs, which are pretty resource intensive to run).

Link: dart-lang/language#1847 (comment)

@yulingtianxia
Copy link
Member

yulingtianxia commented Jan 5, 2022

@fzyzcjy
Thank you very much for your feedback. Before this, we had found that GC triggers were not timely and we didn't find a better way to do it. In earlier versions we provided the release method to manually release associated ObjC/Java objects. In later versions we removed it, and it may be time to add it back.

I think the Dart VM should provide an interface for setting size of external objects.

@fzyzcjy
Copy link
Author

fzyzcjy commented Jan 5, 2022

@yulingtianxia You are welcome!

@fzyzcjy
Copy link
Author

fzyzcjy commented Jan 5, 2022

I think the Dart VM should provide an interface for setting size of external objects.

I guess yes. https://github.com/dart-lang/sdk/blob/main/runtime/include/dart_api.h#L568

/**
 * Updates the external memory size for the given finalizable handle.
 *
 * The caller has to provide the actual Dart object the handle was created from
 * to prove the object (and therefore the finalizable handle) is still alive.
 *
 * May trigger garbage collection.
 */
DART_EXPORT void Dart_UpdateFinalizableExternalSize(
    Dart_FinalizableHandle object,
    Dart_Handle strong_ref_to_object,
    intptr_t external_allocation_size);

and

/**
 * Updates the external memory size for the given weak persistent handle.
 *
 * May trigger garbage collection.
 */
DART_EXPORT void Dart_UpdateExternalSize(Dart_WeakPersistentHandle object,
                                         intptr_t external_allocation_size);

@yulingtianxia
Copy link
Member

yulingtianxia commented Jan 5, 2022

I think the Dart VM should provide an interface for setting size of external objects.

I guess yes. https://github.com/dart-lang/sdk/blob/main/runtime/include/dart_api.h#L568

/**
 * Updates the external memory size for the given finalizable handle.
 *
 * The caller has to provide the actual Dart object the handle was created from
 * to prove the object (and therefore the finalizable handle) is still alive.
 *
 * May trigger garbage collection.
 */
DART_EXPORT void Dart_UpdateFinalizableExternalSize(
    Dart_FinalizableHandle object,
    Dart_Handle strong_ref_to_object,
    intptr_t external_allocation_size);

and

/**
 * Updates the external memory size for the given weak persistent handle.
 *
 * May trigger garbage collection.
 */
DART_EXPORT void Dart_UpdateExternalSize(Dart_WeakPersistentHandle object,
                                         intptr_t external_allocation_size);

This helps us a lot! Thank you!
These symbols are also exposed in dart_api_dl.h.
@hui19 We can estimate the memory footprint of C++/OC and Java objects/data on the heap.

@fzyzcjy
Copy link
Author

fzyzcjy commented Jan 5, 2022

You are welcome!

btw

We can estimate the memory footprint of C++/OC and Java objects/data on the heap.

I am curious about it: how will you do it? e.g. struct S {char* my_bytes;}; How can you know how large it is?

Moreover, how to automatically call UpdateExternalSize whenever the size changes? e.g. S s; s.my_bytes = ...allocate 1MB...; ...; s.my_bytes = ...allocate 1GB...; How will DartNative automatically notice the size is changed from 1MB to 1GB and tell dart?

@yulingtianxia
Copy link
Member

I'm not looking for the perfect solution yet, but it'll work out.

@fzyzcjy
Copy link
Author

fzyzcjy commented Jan 6, 2022

@yulingtianxia Looking forward to seeing the solution

@yulingtianxia
Copy link
Member

@fzyzcjy
Copy link
Author

fzyzcjy commented Apr 20, 2022

@yulingtianxia Sounds interesting. I originally want to do similar things in Rust but now seems that it is not possible. Rust seems not to have such "reflection"-like things.

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

No branches or pull requests

2 participants