Big question, delegates have an iceberg of code associated with them in the CLR. So just some hints. Download the SSCLI20 distribution to look at the source. All relevant code is in the clr/src/vm subdirectory.
UMEntryThunk is the wrapper for a thunk that marshals the call. It is created by COMDelegate::ConvertToCallback() in comdelegate.cpp, called by MarshalNative::GetFunctionPointerForDelegateInternal(), the internal implementation of the Marshal method.
A pointer to UMEntryThunk is stored in the syncblk for the delegate object, syncblk.h, InteropSyncBlockInfo::SetUMEntryThunk() method.
When the garbage collector destroys the delegate object, it also cleans up the syncblk for it and that invokes the ~InteropSyncBlockInfo destructor. Which calls the UMEntryThunk::FreeUMEntryThunk() method which cleans up the thunk again.
So, no, there is no memory leak. Not exactly a finalizer, just part of the normal GC cleanup.