Una máquina virtual, como JVM, es un programa que acepta como entrada, generalmente archivos, un conjunto de instrucciones simples (que generalmente son fáciles de convertir en instrucciones reales de CPU), y en realidad las compila y ejecuta como instrucciones de CPU nativas (generalmente usando un compilador a pedido como HotSpot o JIT).
Es esencialmente una capa de abstracción. Por lo general, es mucho más fácil portar implementaciones de conjuntos de instrucciones de VM a diferentes arquitecturas de procesador, debido a varias similitudes (como estar basado en la pila). También es mucho más fácil portar diferentes lenguajes de programación a las instrucciones de VM, ya que está más orientado hacia los lenguajes de programación modernos que las instrucciones primitivas de la CPU. Muchas máquinas virtuales, como JVM y CLR (.NET) contienen instrucciones para llamar a métodos virtuales y crear instancias de objetos.
Así que tomemos un idioma por ejemplo. Llámalo MyLanguage. Como es un lenguaje de programación, en última instancia, se compila en un conjunto de instrucciones de arquitectura de CPU. Eso significa que, dado un conjunto de instrucciones de máquina virtual flexible y compatible, también es posible compilar MyLanguage a un conjunto de instrucciones de esa VM.
Siempre hay una cuestión de eficiencia, ya que es posible que necesite hackear algunas soluciones en los conjuntos de instrucciones de VM que no tendría que hacer de forma nativa, pero aún es posible.