Uma máquina virtual, como a JVM, é um programa que aceita como entrada, geralmente arquivos, um conjunto de instruções simples (que geralmente são fáceis de converter em instruções reais da CPU) e as compila e executa como instruções nativas da CPU (geralmente usando um compilador sob demanda, como HotSpot ou JIT).
É essencialmente uma camada de abstração. Geralmente, é muito mais fácil portar implementações de conjuntos de instruções da VM para diferentes arquiteturas de processador, devido a várias semelhanças (como ser baseado em pilha). Também é muito mais fácil portar diferentes linguagens de programação para instruções da VM, pois é mais orientado para linguagens de programação modernas do que as instruções primitivas da CPU. Muitas máquinas virtuais, como a JVM e o CLR (.NET), contêm instruções para chamar métodos virtuais e criar instâncias de objetos.
Então, vamos pegar um idioma, por exemplo. Chame de MyLanguage. Por ser uma linguagem de programação, ela compila até um conjunto de algumas instruções da arquitetura da CPU. Isso significa que, dado um conjunto de instruções da Máquina Virtual compatível e flexível, também é possível compilar o MyLanguage para um conjunto de instruções dessa VM.
Sempre há uma questão de eficiência, pois você pode precisar hackear algumas soluções alternativas nos conjuntos de instruções da VM que você não precisaria executar de forma nativa, mas ainda é possível.