Shared Ownership Rarely Makes Sense
This answer might be slightly off-tangent, but I have to ask, how many cases does it make sense from a user-end standpoint to share ownership? At least in the domains I've worked in, there were practically none because otherwise that would imply that the user doesn't need to simply remove something one time from one place, but explicitly remove it from all relevant owners before the resource is actually removed from the system.
It's often a lower-level engineering idea to prevent resources from being destroyed while something else is still accessing it, like another thread. Often when a user requests to close/remove/delete something from the software, it should be removed as soon as possible (whenever it is safe to remove), and it certainly shouldn't linger around and cause a resource leak for as long as the application is running.
As an example, a game asset in a video game might reference a material from the material library. We certainly don't want, say, a dangling pointer crash if the material is removed from the material library in one thread while another thread is still accessing the material referenced by the game asset. But that doesn't mean it makes any sense for game assets to share ownership of materials they reference with the material library. We don't want to force the user to explicitly remove the material from both asset and material library. We just want to make sure that materials are not removed from the materiel library, the sole sensible owner of materials, until other threads are finished accessing the material.
Resource Leaks
Yet I worked with a former team that embraced GC for all components in the software. And while that really helped in making sure we never had resources being destroyed while other threads were still accessing them, we instead ended up getting our share of resource leaks.
And these were not trivial resource leaks of a kind that upsets only developers, like a kilobyte of memory leaked after an hour-long session. These were epic leaks, often gigabytes of memory over an active session, leading to bug reports. Because now when a resource's ownership is being referenced (and therefore shared in ownership) among, say, 8 different parts of the system, then it only takes one to fail to remove the resource in response to the user requesting it to be removed for it to be leaked and possibly indefinitely.
So I was never a huge fan of GC or reference counting applied at any wide scale because of how easy they made it to create leaky software. What would have formerly been a dangling pointer crash which is easy to detect turns into a very difficult-to-detect resource leak which can easily fly under the radar of testing.
Weak references can mitigate this issue if the language/library provides these, but I found it difficult to get a team of developers of mixed skillsets to be able to consistently use weak references whenever appropriate. And this difficulty wasn't just related to the internal team, but to every single plugin developer for our software. They too could easily cause the system to leak resources by just storing a persistent reference to an object in ways that made it difficult to trace back to the plugin as the culprit, so we also got our lion's share of bug reports resulting from our software resources being leaked simply because a plugin whose source code was outside of our control failed to release references to those expensive resources.
Solution: Deferred, Periodic Removal
So my solution later on which I applied to my personal projects that gave me kind of the best I found from both worlds was to eliminate the concept that referencing=ownership
but still have deferred destruction of resources.
As a result, now whenever the user does something that causes a resource to need removal, the API is expressed in terms of just removing the resource:
ecs->remove(component);
... which models the user-end logic in a very straightforward way. However, the resource (component) may not be removed right away if there are other system threads in their processing phase where they could be accessing the same component concurrently.
So these processing threads then yield time here and there which allows a thread which resembles a garbage collector to wake up and "stop the world" and destroy all resources which were requested to be removed while locking out threads from processing those components until it is finished. I've tuned this so that the amount of work needing to be done here is generally minimal and doesn't cut noticeably into frame rates.
Now I can't say this is some tried and tested and well-documented method, but it's something I've been using for a few years now with no headaches whatsoever and no resource leaks. I recommend exploring approaches like this when it's possible for your architecture to fit this kind of concurrency model as it's a lot less heavy-handed than GC or ref-counting and doesn't risk these types of resource leaks flying under the radar of testing.
The one place where I found ref-counting or GC to be useful is for persistent data structures. In that case it's data structure territory, far divorced from user-end concerns, and there it does actually make sense for each immutable copy to potentially be sharing ownership of the same unmodified data.