From c86118aacb5736894845a58b0efed9807b64b073 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 27 Aug 2026 17:11:44 +0200 Subject: [PATCH 1/2] [native] Use a raw pointer for the CoreCLR host's Timing instance `Host::_timing` is a process-lifetime singleton that is created once, when fast timing is enabled, and never released. Holding it in a `std::shared_ptr` bought us nothing but a control block allocation and atomic refcount traffic on every `get_timing ()` call - the call sites even bound the returned shared pointer to a `const&` to avoid the refcount, with a comment apologising for it. Store a plain `Timing*` instead. This removes the last `std::shared_ptr` from the CoreCLR host and drops five `std::__ndk1::__shared_weak_count` references (plus a static initialisation guard) from `libnet-android.release-static-release.a`, taking the host from 55 to 48 undefined libc++ symbols. The allocation itself is unchanged; removing the remaining `new`/`delete` calls is tracked separately. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0a35a0db-502d-48c0-8468-e73b5dd0ab2e --- src/native/clr/host/host.cc | 2 +- src/native/clr/host/internal-pinvokes-clr.cc | 11 ++++------- src/native/clr/include/host/host.hh | 4 ++-- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/native/clr/host/host.cc b/src/native/clr/host/host.cc index f7895fd365d..bda59ec9cc5 100644 --- a/src/native/clr/host/host.cc +++ b/src/native/clr/host/host.cc @@ -316,7 +316,7 @@ void Host::Java_mono_android_Runtime_initInternal ( FastTiming::initialize ((Logger::log_timing_categories() & LogTimingCategories::FastBare) != LogTimingCategories::FastBare); if (FastTiming::enabled ()) [[unlikely]] { - _timing = std::make_shared (); + _timing = new Timing (); internal_timing.start_event (TimingEventKind::TotalRuntimeInit); } diff --git a/src/native/clr/host/internal-pinvokes-clr.cc b/src/native/clr/host/internal-pinvokes-clr.cc index 844f9b748f0..f547387c67a 100644 --- a/src/native/clr/host/internal-pinvokes-clr.cc +++ b/src/native/clr/host/internal-pinvokes-clr.cc @@ -41,11 +41,8 @@ _monodroid_lookup_replacement_method_info (const char *jniSourceType, const char managed_timing_sequence* monodroid_timing_start (const char *message) { - // Technically a reference here is against the idea of shared pointers, but - // in this instance it's fine since we know we won't be storing the pointer - // and this way things are slightly faster. - std::shared_ptr const &timing = Host::get_timing (); - if (!timing) { + Timing *timing = Host::get_timing (); + if (timing == nullptr) { return nullptr; } @@ -64,8 +61,8 @@ void monodroid_timing_stop (managed_timing_sequence *sequence, const char *messa return; } - std::shared_ptr const &timing = Host::get_timing (); - if (!timing) [[unlikely]] { + Timing *timing = Host::get_timing (); + if (timing == nullptr) [[unlikely]] { return; } diff --git a/src/native/clr/include/host/host.hh b/src/native/clr/include/host/host.hh index a492ecf21b6..23b37411f77 100644 --- a/src/native/clr/include/host/host.hh +++ b/src/native/clr/include/host/host.hh @@ -23,7 +23,7 @@ namespace xamarin::android { static void Java_mono_android_Runtime_registerNatives (JNIEnv *env, jclass nativeClass) noexcept; static void propagate_uncaught_exception (JNIEnv *env, jobject javaThread, jthrowable javaException) noexcept; - static auto get_timing () -> std::shared_ptr + static auto get_timing () noexcept -> Timing* { return _timing; } @@ -54,7 +54,7 @@ namespace xamarin::android { private: static inline void *clr_host = nullptr; static inline unsigned int domain_id = 0; - static inline std::shared_ptr _timing{}; + static inline Timing *_timing = nullptr; static inline bool found_assembly_store = false; static inline jnienv_register_jni_natives_fn jnienv_register_jni_natives = nullptr; static inline jnienv_propagate_uncaught_exception_fn jnienv_propagate_uncaught_exception = nullptr; From cc97080381ecac68458aeb387ad2f1dbc20c93d5 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 27 Aug 2026 17:40:56 +0200 Subject: [PATCH 2/2] Address code review feedback Document that `Host::_timing` is intentionally never freed, so that the missing `delete` isn't mistaken for a leak and "fixed" later. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0a35a0db-502d-48c0-8468-e73b5dd0ab2e --- src/native/clr/include/host/host.hh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/native/clr/include/host/host.hh b/src/native/clr/include/host/host.hh index 23b37411f77..96517f1a3b9 100644 --- a/src/native/clr/include/host/host.hh +++ b/src/native/clr/include/host/host.hh @@ -54,6 +54,8 @@ namespace xamarin::android { private: static inline void *clr_host = nullptr; static inline unsigned int domain_id = 0; + // Allocated once, if fast timing is enabled, and intentionally never freed: the instance + // is used for the whole lifetime of the process and released by the OS when it exits. static inline Timing *_timing = nullptr; static inline bool found_assembly_store = false; static inline jnienv_register_jni_natives_fn jnienv_register_jni_natives = nullptr;