Option 2 is quite how the C# Garbadge Collector works. Memory is managed in an indirection, so instead of providing a pointer to the real data in heap, C# provides a pointer (aka object) that refers to a small fixed size section of memory. This section contains data that has for example the TypeId, reference count and lock-bit of an object but also a pointer to the raw object data. So the GC is able to move stuff around in the heap without fthe need to fix a lot of references.
In your case it depends, a resource as CPU related data needs the pointer to the content while a GPU related resource has for example the TextureHandle you got from the graphics API. If you instead give all your handles a pointer to this small “resource header” memory location, you're able to operate on the ref-count using for example atomic increment/decrement without the need to call the manager and the manager will be able to iterate through all of these headers and can clean those not used/referenced anymore