Virtual machines, those magical bits of software that execute bytecodes, are such an excellent solution to so many problems that they have become, in many ways, the default solution where portability is an overriding consideration and performance is not critical. While VMs have been a part of computing for almost 50 years, they did not really become widely popular until the advent of Perl, Python, Java, and .NET. The last two, of course, established VMs as a standard platform for program execution and demonstrated what was suspected but not proven that VMs could be optimized to run code at speeds comparable to native code.
But I'm not close to done. The font hinting in TrueType uses a VM, SQLite uses one, and Valgrind that most excellent tool for C programmers uses one, too. Given this profusion and I am surely overlooking other instances it's incumbent to reconsider the VM's role in software development.
About five years ago, there was a sudden surge in the popularity of DSL creation. Languages such as Groovy and Scala made it easy to invent simple languages that implemented niche programmatic solutions. These tools satisfied an urge that all developers feel at one time or another, which is to be able to express problems and solutions in the domain language. Such DSLs are fine as far as they go, but they frequently have a certain artificiality to their syntax that feels like forcing a fit that does not naturally exist.
Virtual machines represent a more holistic solution. The expressivity is now unlimited since any term can be used as part of the language and, importantly, the VM can customize program execution to fit exactly with the requirements of the domain. For example, the Lua VM is designed to be small and capable of quick loading to execute small programs. It's not designed for parallel programming and so does not support multithreading (which is a forte of the Erlang VM).
But more than the ability to implement domain-specific idioms well, the factors that make VMs so popular are their portability and the ease with which they can be written. The portability is well known. To port a language to a new platform, only the VM need be compiled for the new target and all existing programs should, in theory, work unmodified. (To be accurate, for large VM platforms, like the JVM, this porting is considerably more complicated.)
Ease of implementation is also attractive. Many VMs are small affairs that typically do not exceed 10-20K LOC, many of them being even smaller. And the basic model a giant switch statement that reads the bytecodes and branches to the corresponding execution routine is easy to understand and implement. Only when performance is critical and compiling on-the-fly using a JIT is required do VMs become complex tools to implement.
Because VMs are highly customized to the language and domain they're designed for, they tend to be poor at running multiple languages. They often contain language-specific assumptions in their execution model and they support only a limited set of types (note, for example, the absence of unsigned integers in Java). VM projects that aim to run numerous languages tend not to fare well (Parrot, for example) with the notable exception of the LLVM, which succeeded primarily due to substantial investments and a large, active community.
Because of this specificity and the comparative simplicity of writing a new VM, I expect they will continue to proliferate on the systems we use daily and be increasingly implemented within many other technologies.