Really interesting question... really broad but I'll try my best to give decent input.
Going further, can this observation be made about Garbage Collector implementations in general or are there certain design decisions (in Java or elsewhere) which especially foster or mandate these size increases?
Well java's garbage collector initially didn't support generations, so adding this feature made it grow in size. One other thing that adds to the size/complexity of the jvm garbage collector is its configuration. The user can tweak the gc in a number of ways which increases the complexity. See this doc if you really want to know all the tun-able features of the jvm garbage collector http://www.oracle.com/technetwork/java/javase/gc-tuning-6-140523.html
This stackoverflow answer goes into this in a bit more depth https://stackoverflow.com/a/492821/25981
As to comparing size vs features...
Here is a very simple garbage collector for C:
https://code.google.com/p/dpgc/
It has very few features and even requires the user to mark blocks as references are shared. It's size is very small weighing in at one C
file and one header
file.
Compare this to a fully featured gc as used in the .net framework. Below I've included a bunch of talks with the two architects of the .net garbage collector.
http://channel9.msdn.com/Tags/garbage+collector
Specifcally this link: http://channel9.msdn.com/Shows/Going+Deep/Patrick-Dussud-Garbage-Collection-Past-Present-and-Future they discuss the evolution of the .net gc both interns of featuers and complexity( which is related to lines of code.)