This was found in Shenandoah testing after JDK-8224115 that exposed it to the mercy of CodeCache lock. There is apparently a path via ParallelCleaningTask that calls into nmethod::make_unloaded without the lock, which in turn calls CollectedHeap::unregister_nmethod. This definitely happens with Shenandoah, and might also happen with G1? I cannot see where CodeCache lock is taken on CH::unregister_nmethod path in G1.
# A fatal error has been detected by the Java Runtime Environment:
#
# Internal Error (/home/jenkins/workspace/nightly/jdk-jdk/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp:159), pid=82156, tid=82172
# assert(CodeCache_lock->owned_by_self()) failed: Must own CodeCache_lock
...
--------------- T H R E A D ---------------
Current thread (0x00007fd43c007000): GCTaskThread "Shenandoah GC Threads#4" [stack: 0x00007fd4440da000,0x00007fd4441da000] [id=82172]
Stack: [0x00007fd4440da000,0x00007fd4441da000], sp=0x00007fd4441d8b30, free space=1018k
Native frames: (J=compiled Java code, A=aot compiled Java code, j=interpreted, Vv=VM code, C=native code)
V [libjvm.so+0x15c0d74] ShenandoahCodeRoots::remove_nmethod(nmethod*)+0x474
V [libjvm.so+0x136d5d0] nmethod::make_unloaded()+0x3a0
V [libjvm.so+0x1441419] ParallelCleaningTask::work(unsigned int)+0xc9
V [libjvm.so+0x18fc7e6] GangWorker::run_task(WorkData)+0x66
V [libjvm.so+0x18fc8d8] GangWorker::loop()+0x48
V [libjvm.so+0x17d756b] Thread::call_run()+0xfb
V [libjvm.so+0x13fdac9] thread_native_entry(Thread*)+0x119