dart_native icon indicating copy to clipboard operation
dart_native copied to clipboard

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

Open fzyzcjy opened this issue 4 years ago • 9 comments

Hi, I was implementing the fully automatic memory management between Dart/Flutter and Rust (see https://github.com/fzyzcjy/flutter_rust_bridge/issues/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: https://github.com/dart-lang/language/issues/1847#issuecomment-1002860171

fzyzcjy avatar Jan 05 '22 00:01 fzyzcjy

@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.

yulingtianxia avatar Jan 05 '22 08:01 yulingtianxia

@yulingtianxia You are welcome!

fzyzcjy avatar Jan 05 '22 08:01 fzyzcjy

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);

fzyzcjy avatar Jan 05 '22 08:01 fzyzcjy

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.

yulingtianxia avatar Jan 05 '22 10:01 yulingtianxia

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?

fzyzcjy avatar Jan 05 '22 10:01 fzyzcjy

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

yulingtianxia avatar Jan 06 '22 14:01 yulingtianxia

@yulingtianxia Looking forward to seeing the solution

fzyzcjy avatar Jan 06 '22 14:01 fzyzcjy

@yulingtianxia Looking forward to seeing the solution

https://github.com/dart-native/dart_native/blob/master/dart_native/ios/Classes/NSObject%2BDartHandleExternalSize.m

yulingtianxia avatar Apr 20 '22 07:04 yulingtianxia

@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.

fzyzcjy avatar Apr 20 '22 09:04 fzyzcjy