All runtimes need to erase types at some level. Otherwise ArrayList<Foo> and ArrayList<Bar> would end up compiling identical versions if both Foo and Bar are reference-only types, which just wastes memory. At some level the compiler and runtime need to merge duplications - in C++ that feature is called COMDAT folding, or used to be.
.NET has had serious problems with code duplication in the past. Here's an excellent blog post by a Microsoft engineer on it:
http://joeduffyblog.com/2011/10/23/on-generics-and-some-of-t...