The GC itself does not call finalize. Instead, when the GC finds a "finalizable" object to which no strong rooted references exist, it marks it "non-finalizable" and moves it to a queue of objects which need to be finalized immediately and if necessary starts a thread which will process each item in the queue. The queue itself will act as a strong rooted reference to objects whose finalize methods haven't been run, and the execution context of the currently-running finalizer will keep it alive, but once control leaves the finalizer, the object will become eligible for collection unless a strong rooted reference to it has been stored elsewhere.
The finalizer need not actually do anything to enable the GC to collect an object. The GC itself clears the "finalizable" flag when the object gets moved to the queue of objects which need immediate finalization, and the act of pulling an object out of the queue in order to run it will eliminate the queue as a rooted reference. By the time the finalizer executes, its execution context will be the only thing keeping the object alive, so leaving that execution context via any means--exceptional or otherwise--will make the object eligible for garbage collection.
Note that the real purpose of a finalize method is not to have objects do things to themselves, but rather to allow objects to notify outside entities acting on their behalf that their services are no longer required. If a finalizer dies without delivering such notifications, the notifications will not be sent.