Repository navigation
jcmd class_reload
ceragon edited this page Jun 24, 2022
·
4 revisions
public final class ClassReloadAgent {
private static Instrumentation inst = null;
public static void premain(String agentArgs, Instrumentation i) {
inst = i;
}
}jboolean
invokeJavaAgentMainMethod( JNIEnv * jnienv,
jobject instrumentationImpl,
jmethodID mainCallingMethod,
jstring className,
jstring optionsString) {
jboolean errorOutstanding = JNI_FALSE;
if ( mainCallingMethod != NULL ) {
// 调用 java 代码中的 premain 方法
(*jnienv)->CallVoidMethod( jnienv,
instrumentationImpl,
mainCallingMethod,
className,
optionsString);
errorOutstanding = checkForThrowable(jnienv);
}
return !errorOutstanding;
}由于 instrumentationImpl 参数是关注的重点,因此查看一下它的赋值和调用过程
jboolean
startJavaAgent( JPLISAgent * agent,
JNIEnv * jnienv,
const char * classname,
const char * optionsString,
jmethodID agentMainMethod) {
// agent->mInstrumentationImpl ,因此再查看 agent 的调用
if (success) {
success = invokeJavaAgentMainMethod( jnienv,
agent->mInstrumentationImpl,
agentMainMethod,
classNameObject,
optionsStringObject);
}
return success;
}jboolean
processJavaStart( JPLISAgent * agent,
JNIEnv * jnienv) {
jboolean result;
if ( result ) {
// 很明显,InstrumentationImpl 在这里赋值了
result = createInstrumentationImpl(jnienv, agent);
}
if ( result ) {
// 调用上面的方法
result = startJavaAgent(agent, jnienv,
agent->mAgentClassName, agent->mOptionsString,
agent->mPremainCaller);
}
}jboolean
createInstrumentationImpl( JNIEnv * jnienv,
JPLISAgent * agent) {
jobject localReference = NULL;
// peerReferenceAsScalar: 对应的就是 InstrumentationImpl 的 mNativeAgent
// 因此还是得看 agent 的产生
jlong peerReferenceAsScalar = (jlong)(intptr_t) agent;
localReference = (*jnienv)->NewObject( jnienv,
implClass,
constructorID,
peerReferenceAsScalar,
agent->mRedefineAdded,
agent->mNativeMethodPrefixAdded);
resultImpl = (*jnienv)->NewGlobalRef(jnienv, localReference);
agent->mInstrumentationImpl = resultImpl;
}void JNICALL
eventHandlerVMInit( jvmtiEnv * jvmtienv,
JNIEnv * jnienv,
jthread thread) {
JPLISEnvironment * environment = NULL;
environment = getJPLISEnvironment(jvmtienv);
success = processJavaStart( environment->mAgent, jnienv);
}JPLISEnvironment *
getJPLISEnvironment(jvmtiEnv * jvmtienv) {
JPLISEnvironment * environment = NULL;
jvmtierror = (*jvmtienv)->GetEnvironmentLocalStorage(
jvmtienv,
(void**)&environment);
return environment;
}JvmtiEnv::GetEnvironmentLocalStorage(void** data_ptr) {
*data_ptr = (void*)get_env_local_storage();
return JVMTI_ERROR_NONE;
}void JNICALL Java_sun_instrument_InstrumentationImpl_redefineClasses0
(JNIEnv * jnienv, jobject implThis, jlong agent, jobjectArray classDefinitions) {
redefineClasses(jnienv, (JPLISAgent*)(intptr_t)agent, classDefinitions);
}void redefineClasses(JNIEnv * jnienv, JPLISAgent * agent, jobjectArray classDefinitions) {
jvmtiEnv* jvmtienv = jvmti(agent);
jvmtiClassDefinition* classDefs = NULL;
jsize numDefs = 0;
numDefs = (*jnienv)->GetArrayLength(jnienv, classDefinitions);
jvmtiError errorCode = JVMTI_ERROR_NONE;
errorCode = (*jvmtienv)->RedefineClasses(jvmtienv, numDefs, classDefs);
}jvmtiError
JvmtiEnv::RedefineClasses(jint class_count, const jvmtiClassDefinition* class_definitions) {
VM_RedefineClasses op(class_count, class_definitions, jvmti_class_load_kind_redefine);
VMThread::execute(&op);
}class VM_RedefineClasses: public VM_Operation {
bool doit_prologue();
void doit();
void doit_epilogue();
};jvmtiError VM_RedefineClasses::compare_and_normalize_class_versions(
InstanceKlass* the_class,
InstanceKlass* scratch_class) {
// 1. 父类的地址要一致
// 2. 父类不能为空
// 3. 父类的名字得一样
if (the_class->super() != scratch_class->super() &&
(the_class->super() == NULL || scratch_class->super() == NULL ||
the_class->super()->name() !=
scratch_class->super()->name())) {
return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED;
}
// 原类的所有接口
Array<Klass*>* k_interfaces = the_class->local_interfaces();
// 新类的所有接口
Array<Klass*>* k_new_interfaces = scratch_class->local_interfaces();
// 首先接口数量得一样
int n_intfs = k_interfaces->length();
if (n_intfs != k_new_interfaces->length()) {
return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED;
}
// 不仅接口的名字要对上,顺序还不能变
for (i = 0; i < n_intfs; i++) {
if (k_interfaces->at(i)->name() !=
k_new_interfaces->at(i)->name()) {
return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED;
}
}
if (the_class->is_in_error_state()) {
// TBD #5057930: special error code is needed in 1.6
return JVMTI_ERROR_INVALID_CLASS;
}
// 类的属性要一致
jvmtiError err = check_nest_attributes(the_class, scratch_class);
if (err != JVMTI_ERROR_NONE) {
return err;
}
// 类型的访问标志要一致,不能变
jushort old_flags = (jushort) the_class->access_flags().get_flags();
jushort new_flags = (jushort) scratch_class->access_flags().get_flags();
if (old_flags != new_flags) {
return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED;
}
// 检查成员变量是否一致
JavaFieldStream old_fs(the_class);
JavaFieldStream new_fs(scratch_class);
for (; !old_fs.done() && !new_fs.done(); old_fs.next(), new_fs.next()) {
// access
old_flags = old_fs.access_flags().as_short();
new_flags = new_fs.access_flags().as_short();
// JVM_RECOGNIZED_FIELD_MODIFIERS: 是所有支持的访问标志
// 由于异或后相同为0,不同为1. 所以只要异或的结果不为0,那么与上后者就会变为 1
if ((old_flags ^ new_flags) & JVM_RECOGNIZED_FIELD_MODIFIERS) {
return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED;
}
// 成员变量的顺序不允许改变
if (old_fs.offset() != new_fs.offset()) {
return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED;
}
// name and signature
Symbol* name_sym1 = the_class->constants()->symbol_at(old_fs.name_index());
Symbol* sig_sym1 = the_class->constants()->symbol_at(old_fs.signature_index());
Symbol* name_sym2 = scratch_class->constants()->symbol_at(new_fs.name_index());
Symbol* sig_sym2 = scratch_class->constants()->symbol_at(new_fs.signature_index());
// 比较在常量表中的名字是否一样,注意此步骤不严格要求在常量表中的索引一致
if (name_sym1 != name_sym2 || sig_sym1 != sig_sym2) {
return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED;
}
}
// 成员变量的数量不一致
if (!old_fs.done() || !new_fs.done()) {
return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED;
}
// 比较方法的内容是否一致
Array<Method*>* k_old_methods(the_class->methods());
Array<Method*>* k_new_methods(scratch_class->methods());
int n_old_methods = k_old_methods->length();
int n_new_methods = k_new_methods->length();
Thread* thread = Thread::current();
int ni = 0;
int oi = 0;
while (true) {
Method* k_old_method;
Method* k_new_method;
enum { matched, added, deleted, undetermined } method_was = undetermined;
if (oi >= n_old_methods) {
if (ni >= n_new_methods) {
break;
}
// 新增了方法
k_new_method = k_new_methods->at(ni);
method_was = added;
} else if (ni >= n_new_methods) {
k_old_method = k_old_methods->at(oi);
// 删除了方法
method_was = deleted;
} else {
k_old_method = k_old_methods->at(oi);
k_new_method = k_new_methods->at(ni);
// 方法名字得一样
if (k_old_method->name() != k_new_method->name()) {
// 正常情况下,返回值 == 0
if (k_old_method->name()->fast_compare(k_new_method->name()) > 0) {
method_was = added;
} else {
method_was = deleted;
}
} else if (k_old_method->signature() == k_new_method->signature()) {
// 方法的名字一样,签名也匹配
method_was = matched;
} else {
// 出现了不一样的方法,很有可能是新类删除了原类当前的方法
// 如果新类的下一个方法没有匹配上,则说明新类删除了方法,否则就说明新类增加了一个方法
int nj;
for (nj = ni + 1; nj < n_new_methods; nj++) {
// 新类的下一个方法
Method* m = k_new_methods->at(nj);
if (k_old_method->name() != m->name()) {
// 新类删除了方法
method_was = deleted;
break;
}
if (k_old_method->signature() == m->signature()) {
// 匹配上了,说明新类在之前插入了一个新方法
// 下面是在两个方法对调,调整顺序
k_new_methods->at_put(ni, m);
k_new_methods->at_put(nj, k_new_method);
k_new_method = m;
method_was = matched;
break;
}
}
if (nj >= n_new_methods) {
// 新类没有下一个方法了,说明新类就是把方法删了
method_was = deleted;
}
}
}
switch (method_was) {
case matched:
// 比较方法的访问标志是否一样
old_flags = (jushort) k_old_method->access_flags().get_flags();
new_flags = (jushort) k_new_method->access_flags().get_flags();
if ((old_flags ^ new_flags) & ~(JVM_ACC_NATIVE)) {
return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED;
}
{
u2 new_num = k_new_method->method_idnum();
u2 old_num = k_old_method->method_idnum();
// 下面是在调整新类的方法顺序,来保证和原类一致
if (new_num != old_num) {
Method *idnum_owner = scratch_class->method_with_idnum(old_num);
if (idnum_owner != NULL) {
idnum_owner->set_method_idnum(new_num);
idnum_owner->set_orig_method_idnum(k_new_method->orig_method_idnum());
}
// Take current and original idnum from the old_method
k_new_method->set_method_idnum(old_num);
k_new_method->set_orig_method_idnum(k_old_method->orig_method_idnum());
// 可能产生 OOM 异常
if (thread->has_pending_exception()) {
return JVMTI_ERROR_OUT_OF_MEMORY;
}
}
}
++oi;
++ni;
break;
case added:
new_flags = (jushort) k_new_method->access_flags().get_flags();
// 必须是私有的,且 final 和 static 满足其一
if ((new_flags & JVM_ACC_PRIVATE) == 0
// hack: private should be treated as final, but alas
|| (new_flags & (JVM_ACC_FINAL|JVM_ACC_STATIC)) == 0
) {
return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED;
}
{
// 由于新增了方法,所以需要给方法安排一个常量表的位置
u2 num = the_class->next_method_idnum();
if (num == ConstMethod::UNSET_IDNUM) {
// 常量表满了
return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED;
}
// 新增方法的索引
u2 new_num = k_new_method->method_idnum();
// num 是分配给当前方法的位置,查一下这个位置在新类中是否被占了
Method* idnum_owner = scratch_class->method_with_idnum(num);
if (idnum_owner != NULL) {
// 被占了,对调一下位置
idnum_owner->set_method_idnum(new_num);
idnum_owner->set_orig_method_idnum(k_new_method->orig_method_idnum());
}
// 没被占, 设置位置
k_new_method->set_method_idnum(num);
k_new_method->set_orig_method_idnum(num);
if (thread->has_pending_exception()) {
return JVMTI_ERROR_OUT_OF_MEMORY;
}
}
break;
case deleted:
old_flags = (jushort) k_old_method->access_flags().get_flags();
// private final 或者 private static 方法可以删除
if ((old_flags & JVM_ACC_PRIVATE) == 0
|| (old_flags & (JVM_ACC_FINAL|JVM_ACC_STATIC)) == 0
) {
return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_DELETED;
}
break;
}
}
}java 源码如下
public class TestReload {
private final int d = 6;
private static int c = 4;
private final static int b = 3;
private int a = 2;
public void testPrint() {
System.out.println(a);
System.out.println(b);
System.out.println(c);
System.out.println(d);
}
public static void main(String[] args) throws InterruptedException {
TestReload reload = new TestReload();
while (true) {
reload.testPrint();
TimeUnit.SECONDS.sleep(1L);
}
}
}在上面的例子中,只有变量 d 和 b 可以热加载,通过反汇编字节码得到如下结果
0: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: getfield #3 // Field a:I
7: invokevirtual #5 // Method java/io/PrintStream.println:(I)V
10: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
13: iconst_3
14: invokevirtual #5 // Method java/io/PrintStream.println:(I)V
17: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
20: getstatic #7 // Field c:I
23: invokevirtual #5 // Method java/io/PrintStream.println:(I)V
26: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
29: bipush 6
31: invokevirtual #5 // Method java/io/PrintStream.println:(I)V
34: return对应的字节码如下,所以结论就是 final 的基本类型变量,直接被优化成了字节码,而不是 getField。
- a: getfield
- b: iconst_3
- c: getstatic
- d: bipush 6