The sharing of modified bytecode can cause potential problems.
When a class is stored in the cache, the location from which it
was loaded and a time stamp indicating version information are also
stored. When retrieving a class from the cache, the location from
which it was loaded and the time stamp of that location are used to
determine whether the class should be returned. The cache does not
note whether the bytes being stored were modified before they were
defined unless it is specifically told so. Do not underestimate the
potential problems that this modification could introduce:
- In theory, unless all JVMs sharing the same classes are using
exactly the same bytecode modification, JVMs could load incorrect
bytecode from the cache. For example, if JVM1 populates a cache with
modified classes and JVM2 is not using a bytecode modification agent,
but is sharing classes with the same cache, it could incorrectly load
the modified classes. Likewise, if two JVMs start at the same time
using different modification agents, a mix of classes could be stored
and both JVMs will either throw an error or demonstrate undefined
behavior.
- An important prerequisite for caching modified classes is that
the modifications performed must be deterministic and final. In other
words, an agent which performs a particular modification under one
set of circumstances and a different modification under another set
of circumstances, cannot use class caching. This is because only one
version of the modified class can be cached for any given agent and
once it is cached, it cannot be modified further or returned to its
unmodified state.
In practice, modified bytecode can be shared safely
if the following criteria are met:
- Modifications made are deterministic and final (described above).
- The cache knows that the classes being stored are modified in
a particular way and can partition them accordingly.
The VM provides features that allow you to share
modified bytecode safely, for example using "modification contexts".
However, if a JVMTI agent is unintentionally being used with shared
classes without a modification context, this usage does not cause
unexpected problems. In this situation, if the VM detects the presence
of a JVMTI agent that has registered to modify class bytes, it forces
all bytecode to be loaded from disk and this bytecode is then modified
by the agent. The potentially modified bytecode is passed to the cache
and the bytes are compared with known classes of the same name. If
a matching class is found, it is reused; otherwise, the potentially
modified class is stored in such a way that other JVMs cannot load
it accidentally. This method of storing provides a "safety net" that
ensures that the correct bytecode is always loaded by the JVM running
the agent, but any other JVMs sharing the cache will be unaffected. Performance
during class loading could be affected because of the amount of checking
involved, and because bytecode must always be loaded
from disk. Therefore, if modified bytecode is being intentionally
shared, the use of modification contexts is recommended.