Hi Tim. Welcome.
What processing the finalizer does is up to you. That flexibility is supported by having a valid construct (the object in its current state) to finalize. This is always true for your own types that use finalization, and should be irrelevant in your use of other’s types that use finalization. Of course, finalization is not necessary unless it is.
Finalization should never occur while there remains purposeful utility to the persistence of an object. And where a datatype design uses finalization, it is very dangerous to consider subverting expectation: a constructed object becomes available for garbage collection (Julia manages this) and only after that (and before it is garbage collected) does the finalization function run.
Some sorts of finalization do not involve actively managing locks, and that’s nice. Often, the work of finalization must be done more carefully. See Safe use of Finalizers for insight into the use of locks.
Use finalizers to finalize the disposition of object that has been constructed from your structured type. One of the most frequent uses of finalization is when interfacing with a C or other language library that performs its own memory allocation and release to provide us with the interfaced object. We have to ensure the library object in C and also in its Julia clothing stay together, even when they disappear.
Of course, you may have other reasons to finalize objects … say, checking to see if its appropriate to do some tree rebalancing … just do not try to supervise the when of it.