それぞれの登場時期には、既に C++言語が、gccによってどんなCPUに対しても
native binaryを出力できるようになっていたのに、どうしてJavaやC#がどうして
native binaryを出さずに、仮想コードを出す仕様にしたかについては誰でも
時々疑問に思うと思うんですが、自分なりに出した答えは:

1. gccは有ったが、それをバックエンドに使おうとすると、gcc のソースの一部
 をJavaやC#のコンパイラの中に組み込むか、中間コードを C 言語の形式で出力して
 gccをバックエンドとして動かす必要があった。これはライセンス、gcc環境のサイズの
 大きさ、言語処理系会社としてのプライドなどの観点から問題があった。

2. 実は、そもそも gcc が対応していないアーキテクチャも世の中にはあって、
 その環境でも Java や C# を動かしたい場合には、駄目であった。
 また、新しいCPUが出てきたときには、gccの方を修正しなくてはならなく
 なるが、gccのソースを解読するのは難しいので難しい。

3. C言語が対応しているCPUであっても、そもそも、プログラミングのフレームワーク的な
 構造が、Windowsとは全く異なっているプラットフォームが時々ありえる。
 例えば時代が違うかもしれないが、Objective-Cの環境でWindows風のプログラミングを
 するのは難しい。そもそも、GUIプログラムは特殊言語で書くようになっているプラット
 フォームや、Waitや効率的なメッセージループを書けないプラットフォームが存在しており、
 そのような環境に同じソースコードで書いたプログラムを移植するには、インタプリタ的にも
 動作しうる仮想コードで無いと困ることがある。