Any thoughts to pre-allocating larger blocks of memory, and using those, instead of hitting the wall with memory fragmentation?
Something more akin to the object pool pattern would probably be more helpful and squeeze performance out of your engine before even further tweaks would be needed.
Automatic garbage collecting is (usually) something to be avoided in the world of optimizing game engines, as I'm sure you're learning. I'm glad your team is making strides!
For sure. We use a fair amount of object pooling, both with GameObjects (like cards) to avoid instantiation costs and with Unity-agnostic data types (like Lists) to avoid allocations.