JDK-8004815 : NPG: Using raw Method* is unsafe when the method_holder has been redefined
  • Type: Enhancement
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: hs25
  • Priority: P3
  • Status: Resolved
  • Resolution: Won't Fix
  • Submitted: 2012-12-10
  • Updated: 2017-10-27
  • Resolved: 2017-10-27
Related Reports
Relates :  
Relates :  
Before NPG the GC would find all used methodOopDescs by tracing through the heap. With NPG Methods are not java Objects anymore, and the GC don't find them by normal tracing of the heap.

The users of Method* needs to do two thing:
1) Make sure that the Klass is kept alive by exposing either the mirror or the class loader to the GC.
2) Make sure that the the JVM still knows that the Method is being used.

If (1) is missed, we might unload the Klass prematurely.
If (2) is missed, the method can be deallocated if it has been redefined.

We currently have two options to handle (2).
a) Make sure that Method::set_on_stack(true) is called during MetadataOnStackMark()
b) Use a methodHandle instead of a Method*.

We need to make sure that uses of Method* adhere to these requirements. Preferably, by some code constructs rather then by convention.
There doesn't seem to be a good way to fix this problem. If we think of something, we can reopen this. Closing as WNF.

Some correction to #1 is that GC doesn't handle Method* in the Java heap. The only ones left are Method* in java_lang_invoke_MemberName which are dealt with in a side table.

No current bugs traceable to this issues right now, but maybe we can do something in the jdk9 timeframe to make the code safer.

There are three categories of Method* references that the VM has to deal with for both GC unloading and for deleting during RedefineClasses. When we implemented Extended Class Redefinition, we will have to worry also about Klass* pointers, but that's going to be more difficult. 1. Oop -> Method* references There are as far as I can see 3 instances of oops that refer directly to Method* pointers. Other oops, such as reflection, refer to the method's idnum which can be reconstructed with the InstanceKlass. The three instances are - java_lang_Throwable::backtrace() which has an array of Method* pointers. See bug 7174978. The others are java_lang_Class::resolved_constructor() which seems completely unused, and java_lang_invoke_MemberName::set_vmtarget(oop mname, Metadata* ref). For GC, the backtrace and java_lang_invoke_MemberName also add the mirror of the Method*. The resolved_constructor is in the mirror. For keeping these Method* from being reclaimed if they are redefined, they must be marked as "alive" "on_stack" or present in some way during a full collection. GC already has a InstanceMirrorKlass so that resolved_constructor() if it's not deleted, can be walked and I think GC should know about java_lang_invoke_MemberName so that it can take special actions when encountering this. One set of backtrace changes creates a side structure to walk to mark these Method* on_stack() but I'm performance testing another version that removes Method* pointers completely. We need to have a specific strategy for dealing with Method* in oops and I think GC actions are the way to go (except my latest version of backtrace won't need it). 2. Activation/Stack -> Method* references If the Method* is actively running on the stack in the Java application when it is being redefined, the VM has to be careful not unload it or to delete it for class redefinition. In the interpreter, the instance of the java_lang_Class is on the stack as the "this" pointer in most cases (or instance of super class) as the receiver. In some special cases, a new GC OopToCLDClosure had to be added for the interpreted frames if the "this" pointer is overwritten (or not present as in the case of invokeStatic_LLL_L type invocations). For compiled methods, the class_loader of the nmethod is added to the oop section of the method, which is walked as a strong root for active frames. There are other cases that I call on-stack Method* pointers also. These are in the compile task queue. Lastly, any Method* that is referenced by the VM native code is considered an on_stack reference to this Method*. Unfortunately, we had to keep methodHandles to hold these in a per-thread side structure so that they are marked on stack and not deleted by GC. This is only a little different from how the VM worked with the PermGen where these pointers could move. The pointers can't move, but if nothing else is referring to them and they are redefined, they can be deleted during class unloading. The MetadataOnStackMark does a walk of the known metadata stack locations and marks them on the stack. If it's interpreted or compiled, a stack walk before redefine classes (to mark which methods are on-stack so that they can be given breakpoints), is also done. These methodHandles have never been very safe! We have the CheckUnhandledOop option which will cause the VM to crash immediately to detect unhandled oops. It only worked on solaris compilers because of the overloading but it should be fixed. This sort of thing should be added specifically for methodHandles also. 3. Metadata -> Metadata references The Method* that Klass* refers to is kept alive because it is contained in the same Metaspace as the class. For the cases of 1 above, the Method* pointers in oops also includes the mirror, which keeps the Klass* alive and that keeps the class_loader alive which keeps the Method* alive for GC. For the cases in #2 it's mostly the same thing. In the cases where Metadata from one class-loader refers to Metadata from another, we create a dependency between the class loaders, which keeps the second alive. For RedefineClasses, the code in jvmtiRedefineClasses walks through all the metadata starting at the InstanceKlass and replaces the old method with the new method. It walks the constant pool, zeros the cpCache entries, walks the vtables, flushes out obsolete entries from the oop_map_cache, etc.