No (to George), the JVM does not attempt to return memory to OS like that. Generally, the JVM retains the data segments and when they are cleared by the garbage collector, the data segments remain as heap space for the JVM to reuse. New objects will be allocated out of the available JVM heap space. Actually, some OS's will allow applications to return memory to the OS while the program is running, and there are some JVMs that claim to support this too.
But back to the original question about the garbage collection working differently depending on it being explicitly vs. implictly started. This can be true, i.e. the garbage collection algorithm can be different for different situations. The garbage collection algorithm is not specified by the Java specification, and each JVM is at liberty to implement its own algorithm. Each newer version of JVM tends to use a more improved algorithm - the latest ones use generational scavenging. Bill Venners' book
Inside the Java 2 Virtual Machine can give you more information if you are interested in garbage collection and JVM internals.
In practice, the algorithm applied is pretty much the same independently of how it is triggered. The System.gc() call essentially sends a message to the garbage collector thread saying "now would be a good time to run a collection and, if you do, tell me when you're finished so I can return". The difference in your application behavior comes from you telling the garbage collector to try harder more often. Without your hints, the garbage collector tends to be lazy, sometimes running in idle time, but mainly not particularly trying hard until available memory gets low. And even when available memory gets low, the garbage collector may decide that the fastest way to provide more memory to the app is to request more from the OS rather than try to clear up as much garbage as possible.
Memory management of Java apps can be confusing. There are two main aspects: firstly minimizing object creation (see the 'Garbage collection problems in java?' discussion thread in this forum); secondly tuning the size of the JVM heap, which you can specify with the -ms/-mx/-Xms/-Xmx options. This second tuning tends to be trial and error. Allowing the memory to get too large means garbage collection may be run less frequently, but will take more time when it is run. Limiting the memory to a smaller size can be better because the garbage collector is forced to run more often, which makes for smaller garbage collections with a lower impact on the app.