"Why is an async model better than using OS threads for an I/O bound workload?"
Because evented/callback-driven code is a nightmare to reason about and breaks lots of very basic tools, like the humble stack trace.
Another big thing for me is resource management - try/finally don't work across callback boundaries, but do work within a virtual thread. I recently ported a netty-based evented system to virtual threads and a very long-standing issue - resource leakage - turned into one very nice try/finally block.