Thanks, Ori. I will work on a reproducible scenario ASAP.
Please note that you wouldn't see a call to any method named Finalize. Instead, if there were an explicit finalizer, it would be named like the constructor, but preceded by ~. So the finalizer, if there were an explicit one for, say, ComponentBase, would be a method named ~ComponentBase.
As I understand it, that method would only ever be called from the GC, and it's purpose would be to invoke Dispose(false) so that the object can be disposed, but (the false part) so that the dispose code knows to NOT try to dispose member objects that may no longer exist (no guaranteed order of cleanup at GC time). Ideally, the developer using the disposable object would call Dispose() when he/she is done (or put their code in a using() block) in which case as your code snippet shows, the object would get disposed, along with objects its managing, and then the object would be marked such that the GC would say "hey, this one already has been properly disposed, so don't put it in the finalization queue -- less work for me!"
In the absence of an explicit finalizer method, GC still will finalize IDisposable objects (if it hasn't been suppressed) by calling Dispose(false) - just no custom code would be executed in that condition (other than what's in Dispose(bool disposing) and not bracketed by if (disposing).
The call to GC.SuppressFinalize(this) is made to prevent GC from putting the object (this) in the queue for finalization. Obviously, that code exists (as you verified, thanks) in ComponentBase. If it were getting called, we shouldn't be seeing so many objects in our finalization queue.
One way for it NOT to be called would be having a Dispose() override anywhere in the chain that does not do a base.Dispose() call. I will look for that in our code, but so far have not seen it.