}
目前已经有很多文章讲具体使用方法了,大家可以Google下,我这里先介绍两篇:
原理探究
========
热替换的核心就在于Instrumentation的两个方法:
void addTransformer(ClassFileTransformer transformer, boolean canRetransform);
void retransformClasses(Class<?>… classes) throws UnmodifiableClassException;
addTransformer()用来注册类的修改器;
retransformClasses()会让类重新加载,从而使得注册的类修改器能够重新修改类的字节码。
下面让我们细细讲讲这两个函数:
3.1: addTransformer()
addTransformer的实现在InstrumentationImpl中:
//sun.instrument.InstrumentationImpl
public synchronized void addTransformer(ClassFileTransformer transformer, boolean canRetransform) {
…
mRetransfomableTransformerManager.addTransformer(transformer);
…
}
上面代码省略了一些,可见我们的ClassFileTransformer又被添加到了TransformerManager中,让我们跟进去看看:
//sun.instrument.TransformerManager
public synchronized void addTransformer( ClassFileTransformer transformer) {
TransformerInfo[] oldList = mTransformerList;
TransformerInfo[] newList = new TransformerInfo[oldList.length + 1];
System.arraycopy( oldList,
0,
newList,
0,
oldList.length);
newList[oldList.length] = new TransformerInfo(transformer);
mTransformerList = newList;
}
ClassFileTransformer对象这次被放入了TransformerManager的一个数组中。
OK,注册完毕,很简单对不对?下面我们再来看下稍微复杂点的retransformClasses()吧。
3.2: retransformClasses()
这个方法的实现是个Native方法。
private native void retransformClasses0(long var1, Class<?>[] var3);
很多同学看到Native方法就头疼,不要急,Native方法也是人写的,不过是一段文本而已。我们来看下他的具体实现吧:
// src/java.instrument/share/native/libinstrument/InstrumentationImplNativeMethods.c
JNIEXPORT void JNICALL
Java_sun_instrument_InstrumentationImpl_retransformClasses0
(JNIEnv * jnienv, jobject implThis, jlong agent, jobjectArray classes) {
retransformClasses(jnienv, (JPLISAgent*)(intptr_t)agent, classes);
}
retransformClasses()最后会调用到 jvmtiEnv.cpp中的RetransformClasses
// src/hotspot/share/prims/jvmtiEnv.cpp
jvmtiError
JvmtiEnv::RetransformClasses(jint class_count, const jclass* classes) {
int index;
JavaThread* current_thread = JavaThread::current();
ResourceMark rm(current_thread);
jvmtiClassDefinition* class_definitions =
NEW_RESOURCE_ARRAY(jvmtiClassDefinition, class_count);
for (index = 0; index < class_count; index++) {
HandleMark hm(current_thread);
jclass jcls = classes[index];
oop k_mirror = JNIHandles::resolve_external_guard(jcls);
…
Klass* klass = java_lang_Class::as_Klass(k_mirror);
jint status = klass->jvmti_class_status();
if (status & (JVMTI_CLASS_STATUS_ERROR)) {
return JVMTI_ERROR_INVALID_CLASS;
}
InstanceKlass* ik = InstanceKlass::cast(klass);
if (ik->get_cached_class_file_bytes() == NULL) {
JvmtiClassFileReconstituter reconstituter(ik);
if (reconstituter.get_error() != JVMTI_ERROR_NONE) {
return reconstituter.get_error();
}
class_definitions[index].class_byte_count = (jint)reconstituter.class_file_size();
class_definitions[index].class_bytes = (unsigned char*)
reconstituter.class_file_bytes();
} else {
// it is cached, get it from the cache
class_definitions[index].class_byte_count = ik->get_cached_class_file_len();
class_definitions[index].class_bytes = ik->get_cached_class_file_bytes();
}
class_definitions[index].klass = jcls;
}
VM_RedefineClasses op(class_count, class_definitions, jvmti_class_load_kind_retransform);
VMThread::execute(&op);
return (op.check_error());
}
上面这段主要干了两件事:
(1) 根据java层的Class对象,找到JVM层的类实例InstanceKlass,并获取类的字节码,存放在class_definitions数组中。因为可以一次替换多个类,所以这里加了一个循环体,遍历每个要修改的类。
(2) 调用VMThread::execute(&op),进入下一步。
VMThread::execute(&op) 中会调用到 VM_RedefineClasses::doit_prologue(),最终调用到VM_RedefineClasses::load_new_class_versions():
jvmtiError VM_RedefineClasses::load_new_class_versions(TRAPS) {
…
InstanceKlass* the_class = get_ik(_class_defs[i].klass);
Symbol* the_class_sym = the_class->name();
ClassFileStream st((u1*)_class_defs[i].class_bytes,
_class_defs[i].class_byte_count,
“VM_RedefineClasses”,
ClassFileStream::verify);
Handle the_class_loader(THREAD, the_class->class_loader());
Handle protection_domain(THREAD, the_class->protection_domain());
state->set_class_being_redefined(the_class, _class_load_kind);
InstanceKlass* scratch_class = SystemDictionary::parse_stream(
the_class_sym,
the_class_loader,
protection_domain,
&st,
…
}
上面这个方法调用了parse_stream(),从文件流中解析类,最终触发类的重新加载:
InstanceKlass* SystemDictionary::load_shared_class(InstanceKlass* ik,
Handle class_loader,
Handle protection_domain, TRAPS) {
…
InstanceKlass* new_ik = KlassFactory::check_shared_class_file_load_hook(
ik, class_name, class_loader, protection_domain, CHECK_NULL);
if (new_ik != NULL) {
return new_ik;
}
…
return ik;
}
这里又调用了KlassFactory::check_shared_class_file_load_hook(),看名字就知道是个hook方法,它会调用post_class_file_load_hook(),利用JvmtiClassFileLoadHookPoster来通知类修改器进行类的修改。
消息的处理者为:eventHandlerClassFileLoadHook():
void JNICALL
eventHandlerClassFileLoadHook( jvmtiEnv * jvmtienv,
JNIEnv * jnienv,
jclass class_being_redefined,
jobject loader,
const char* name,
jobject protectionDomain,
jint class_data_len,
const unsigned char* class_data,
jint* new_class_data_len,
unsigned char** new_class_data) {
JPLISEnvironment * environment = NULL;
environment = getJPLISEnvironment(jvmtienv);
/* if something is internally inconsistent (no agent), just silently return without touching the buffer */
if ( environment != NULL ) {
jthrowable outstandingException = preserveThrowable(jnienv);
transformClassFile( environment->mAgent,
jnienv,
loader,
name,
class_being_redefined,
protectionDomain,
class_data_len,
class_data,
new_class_data_len,
new_class_data,
environment->mIsRetransformer);
restoreThrowable(jnienv, outstandingException);
}
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
总结
互联网大厂比较喜欢的人才特点:对技术有热情,强硬的技术基础实力;主动,善于团队协作,善于总结思考。无论是哪家公司,都很重视高并发高可用技术,重视基础,所以千万别小看任何知识。面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,是不是能真的得到锻炼。其实我写了这么多,只是我自己的总结,并不一定适用于所有人,相信经过一些面试,大家都会有这些感触。
**另外本人还整理收藏了2021年多家公司面试知识点以及各种技术点整理 **
下面有部分截图希望能对大家有所帮助。
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
1712740583179)]
总结
互联网大厂比较喜欢的人才特点:对技术有热情,强硬的技术基础实力;主动,善于团队协作,善于总结思考。无论是哪家公司,都很重视高并发高可用技术,重视基础,所以千万别小看任何知识。面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,是不是能真的得到锻炼。其实我写了这么多,只是我自己的总结,并不一定适用于所有人,相信经过一些面试,大家都会有这些感触。
**另外本人还整理收藏了2021年多家公司面试知识点以及各种技术点整理 **
下面有部分截图希望能对大家有所帮助。
[外链图片转存中…(img-ZyIV9B3H-1712740583180)]
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-labzf2Io-1712740583181)]