Eric Caspole wrote:
In this change, under a flag and off by default, when compilers notice the code cache is getting full, they will call a vm op that
calls new code that attempts to speculatively unload the oldest half of the nmethods (based on the compile job id) by setting the methodOop's _code field to null, and managing a list of methods disconnected in this way by linking the disconnected nmethods by the nmethod's new _saved_nmethod_link field. Then execution resumes. After inline cache cleaning, callers will have to go back to the methodOop to get the verified entry point, or request recompilation. At that point, the disconnected methodOop's _code field can be restored by looking up the saved nmethod ptr in the nmethod _saved_nmethod_link list, and the methodOop/nmethod go back to their normal state.
If a disconnected nmethod is not restored and called by the second sweep cycle after
the one where forced unloading happened, the nmethod will be marked
non-entrant and got rid of by normal sweeping. That gives the app a
few seconds to make progress and call its hottest methods.
We chose to target the oldest half of nmethods due to a customer
experience with a long-running app server, and despite multiple
redeployments of the same web app, something was preventing old
instances of the web app from ever getting unloaded. In that case,
they ran into the code cache full problem so the most recent
redeployment was running interpreter only. We have also observed that
for many applications a lot of methods get compiled and used during
the startup phase that are never used again.
In this change there is also a timer based backoff, default of 30
seconds, so that if the normal state of the app is constantly
triggering unloading, the unloading will stop and it will fall back
to the existing situation of disabling the compiler.