A few other commenters hit the points better, but mostly it comes down to avoiding allocating objects every frame/tick.
In part this is through avoiding LINQ, lambdas, yield return, async etc. (fine to use in code that is run once like startup, or on relatively rare triggers)
Using struct, arrays of struct/value, Span<>, ref, fixed etc. to keep memory access sequential or on stack.
Use mutable structs, avoiding properties, avoiding struct constructors, moving throws into utility methods, etc. to support (the rather weak) optimizer to do more inlining.
And move data processing of arrays of struct with tight loops, like compression, lighting, meshing, pathfinding into a native c++ dll/so or if applicable gpu. (c++ autovec/simd optimizations or much better than what the clr optimizer currently provides)