I believe it is unsafe for CompilerThread to release code buffer in its destructor.
Before CompilerThread reaches its destructor, it has to be removed from Threads list, which made it no longer participates safepoints. It opens up a race window that can result safepoint scanning to stumble over the code buffer just deleted by CompilerThread's destructor.
Although, the destructor takes CodeCache_lock, but safepoint walk does not take this lock, so it is a race.
I have seen this problem during Shenandoah tests on a big machine with many cores.