I think you're overthinking this way too much. I'm writing a game engine, where latency is much more important than in your case. Even 10ms would translate to visible jitter. GC's not an issue, and disabling it permanently is very counterproductive _unless_ you've exhausted all other options (which you're unlikely to, as you can do everything you can do in C++). All you have to do is care about how you allocate and, if GC seems to be an issue, profile to see _where_ the GC is being called most and optimize those allocations.
Basic rules: For classes with one or few instances (singletons, etc.), GC is not an issue. For classes with hundreds-thousands of instances, it might be an issue. Profile. For classes with more instances, it probably is an issue. Profile, reuse instances, use structs, manually allocate. Arrays: To avoid reallocation on append, use array.assumeSafeAppend() before the append (Assumes that the array is not a slice of a bigger array that would get its data overwritten). If appending a lot, use std.array.Appender. If you need manually allocated storage, use std.container.Array, or create a wrapper around malloc/free. I'm using a templated wrapper that allows me to record how much memory/instances was allocated with which types. Destructors: Structs are RAII like in C. And you can easily create a malloc wrapper that will act exactly like new/delete in C++. Classes: call destroy(instance). Calls the dtor, but doesn't deallocate the class (GC will do that later). Not much different from deleting a class allocated through new in C++ (which is what you do most of the time in C++). If you absolutely need to free the memory, create malloc/free wrappers for classes. In my case, classes are used for polymorphic stuff, which are usually single or few-instance objects. Structs are used for most of the many-instance objects (e.g. components of game entities), and are usually stored in manually allocated arrays. GC is _very_ useful for stuff like closures, which can greatly simplify some code. Also, while I maintain that disabling GC completely is a bad idea, it might be useful to disable it in a small area of code _after_ profiling shows that GC is being called too much there. In my YAML parser library, I've found after profiling that 18% of time was spent in GC. Most of that was in a small piece of code that repeatedly allocated memory. Disabling GC before this code and reenabling it after (scope(exit) to avoid leaking disabled GC) resulted in GC taking ~2% of time, because many unnecessary collects were consolidated into one after the GC was reenabled. Memory usage didn't take a hit, because this was actually a fairly small piece of code, not doing too many allocations per call, just called very often.