It could be unmanaged resources though, which the GC has no idea that they are related to the managed objects. A very small and simple .Net object could have opened a thousand unmanaged file handles, allocate large chunks of unmanaged memory with Win32 API that the GC has no idea about. Especially in a tight loop, as in the sample below, you can end up allocating too many resources, bringing the PC to a crawl, before the GC suddenly kicks in and tries to release everything (collect all objects created) in one go:
FUNCTION Start() AS VOID
LOCAL oObject AS SimpleClass
FOR LOCAL n := 1 AS INT UPTO 1000
oObject := SimpleClass{n}
oObject: DoSomeWorkWithIt()
// all of the above 1000 objects are still alive in memory, until the GC kills them all together
NEXT
CLASS SimpleClass IMPLEMENTS IDisposable
CONSTRUCTOR(nID AS INT)
SELF:AllocateHugeAmountOfResources()
DESTRUCTOR()
SELF: Dispose()
METHOD Dispose() AS VOID
SELF:FreeHugeAmountOfResources()
END CLASS
But if you added a oObject: Dispose() at the end of each iteration, resources would be released exactly at the time they are not needed anymore and memory consumption would always remain at the minimum possible.
Even worse, such an object could still be referenced accidentally while is not needed anymore, thus making it impossible to collect it:
FUNCTION Start() AS VOID
LOCAL oObject1,oObject2 AS SimpleClass
oObject1 := SimpleClass{1}
oObject2 := SimpleClass{2}
oObject1:DoSomeWorkWithIt()
oObject2:DoSomeWorkWithIt()
//
// <a lot of other code in here, having nothing to do with those objects>
//
// this line causes those two objects and their resources
// to still be kept alive till here by the Garbage Collector
? oObject1:GetResult() + oObject2:GetResult()
But, by using BEGIN USING,instead you can limit the visibility of the variables to only where they are really needed, force yourself to write more efficient code and make resource management much more efficient:
FUNCTION Start() AS VOID
LOCAL nResult AS INT
BEGIN USING VAR oObject1 := SimpleClass{1}
oObject1:DoSomeWorkWithIt()
nResult := oObject1:GetResult()
END USING
// The resources used by oObject1 have already been freed here, because
// Dispose() was automatically called from the BEGIN...END USING statement
// Also oObject1 is not visible here anymore, so it can not be used accidentally
// and the GC can also completely collect it
BEGIN USING VAR oObject2 := SimpleClass{2}
oObject2:DoSomeWorkWithIt()
nResult += oObject2:GetResult()
END USING
// same for oObject2, it can be collected now
// <a lot of other code in here, having nothing to do with those objects>
When BEGIN USING is used this way, it both limits the scope of the variable and also ensures Dispose() is automatically called, leading to the more "clean" results.
Btw, I am a bit bothered you cannot use my favorite "LOCAL" syntax for BEGIN USING above, will log this.