漫谈JVM热加载技术(三)--- Hotcode2 Adapter

简介: #Hotcode2中各个Adapter介绍 Hotcode2会在JVM启动阶段和应用运行阶段接入class文件的装载,也就是前一篇文章所说的**JVM Init阶段**和**Runtime阶段**。 Adapter按照使用场景也可以分为2类,一种用是在Init阶段,还有一种是用在Runtime阶段。 # 1 JVM Init阶段 Init阶段的Adapter是在AgentMain.

Hotcode2中各个Adapter介绍

Hotcode2会在JVM启动阶段和应用运行阶段接入class文件的装载,也就是前一篇文章所说的JVM Init阶段Runtime阶段
Adapter按照使用场景也可以分为2类,一种用是在Init阶段,还有一种是用在Runtime阶段。

1 JVM Init阶段

Init阶段的Adapter是在AgentMain.redefineJdkClasses 方法中重新定义各种JVM Class的字节码。

1.1 ClassLoaderAdapter

ClassLoaderAdapter是asm ClassVisitor的子类,
URLClassLoaderAdapter在

java.lang.ClassLoader.defineClass(String name, byte[] b, int off, int len,ProtectionDomain protectionDomain)

调用的时候,通过asm aop的方式执行2个方法:

  • CRMManager.registerClassLoader(ClassLoader classLoader) 记录当前classLoader已经被加载过,并且为此classLoader关联一个ClassReloaderManager(用于热加载class),并且使用classLoader初始化hotcode的插件。
  • ClassTransformer.transformNewLoadClass(String className, ClassLoader classLoader, byte[] classfileBuffer) 在一个类首次被加载的时候,对其进行转换

1.2 URLClassLoaderAdapter

URLClassLoader的扩展类,当前 URLClassLoader.findClass(final String name)被调用时,替换为调用URLClassLoaderAdapter.findClass(ClassLoader classLoader, String className)。

如果调用URLClassLoader.findResource或者URLClassLoader.findResources,则由相应的FindResourceModifier和FindResourcesModifier调用ClassLoaderHelper.findResource/findResources方法来完成的功能

1.3 JdkMethodAdapter

JdkMethodAdapter.visitMethod

   if (name.equals("invoke") && desc.equals("(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;")) {
            return new NewInvokeModifier(mv, access, name, desc);
    }

NewInvokeModifier这个类是在 java.lang.reflect.Method.invoke 方法体中加入AOP逻辑,

loadThis();
        invokeStatic(Type.getType(JdkMethodReflectHelper.class), new Method("isNewMethodAccess",
                "(Ljava/lang/reflect/Method;)Z"));

        Label old = newLabel();
        ifZCmp(EQ, old);//判断是否为true

        loadThis();
        loadArg(0);
        loadArg(1);

        invokeStatic(Type.getType(JdkMethodReflectHelper.class), new Method("newMethodInvoke",
                "(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"));
        // unbox(Type.getReturnType(desc));
        returnValue();//调用完成后直接返回,不会计息method.invoke调用。一般是热加载后,类中方法有修改,则调用此逻辑

        mark(old);//继续method.invoke逻辑

如果JdkMethodReflectHelper.isNewMethodAccess返回true,则调用JdkMethodReflectHelper.newMethodInvoke,否则继续method.inoke的逻辑。转义为java代码为:
java.lang.reflect.Method.invoke方法体代码块:

if(JdkMethodReflectHelper.isNewMethodAccess...){
    return JdkMethodReflectHelper.newMethodInvoke...
}
return method.invoke代码调用

1.4 JdkClassAdapter

这个Adapter会对java.lang.Class做很重要的字节码修改,涉及到通过反射获取类信息的方法接口:反射访问当前Class拥有的公私有字段、公私有构造方法、泛型、公私有方法。
这些新增的asm代码主要起以下作用:

  • 1、返回值过滤:例如Class.getDeclaredFields的返回值被GetDeclaredFieldsModifier中的asm代码使用JdkFieldReflectHelper.filterHotCodeField将HotCode2新增的字段过滤掉。
    再例如:Class.getDeclaredMethods和Class.getMethods的返回值被GetDeclaredMethodsModifier注入代码过滤。
GetDeclaredMethodsModifier:
if (opcode == Opcodes.ARETURN) {
          mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(JdkMethodReflectHelper.class),
                  "filterHotCodeMethods", "([Ljava/lang/reflect/Method;)[Ljava/lang/reflect/Method;");
      }
  • 2、native方法体重写,目的是将原生的实现替换为Hotcode2的逻辑。例如:Class.privateGetDeclaredMethods方法体中调用Class.getDeclaredMethods0--这个是native方法,在PrivateGetDeclaredMethodsModifier中通过visitMethodInsn将Class.getDeclaredMethods0的方法体流程替换为:
        CRMManger.isHotCodeTransform4ReloadClass  
        if(返回true)
           JdkMethodReflectHelper.getDeclaredMethods0  
        else
           Class.getDeclaredMethods0

同样类似的处理方式还有Class.initAnnotationsIfNecessary方法。
采用这种方式的原因:对于jdk native方法是没有方法同字节码的,所以ASM没有办法在JdkClassAdapter中直接在visitMethod中对Class.getDeclaredMethods0进行转换,而是在调用的native方法指令触发的时候,visitMethodInsn方法来接收触发事件,进行native方法体重写。

1.5 JdkFieldAdapter

对java.lang.reflect.Field的方法进行代码扩展处理。主要用途在于判断当前的field是否是修改后的class新增/删除,如果是新增的,则需要重新加载此field所在的class文件。

同样还对访问标注在当前field的Annotation的方法Field.declaredAnnotations进行扩展,增加对修改后的class的field上的Annotation变化的支持。

1.6 JdkConstructorAdapter

java.lang.reflect.Constructor的处理类,在Constructor.newInstance方法和Annotation两方面进行扩展。

构造方法也是方法,同样会遇到class修改后新增/删除构造方法的情况,JdkConstructorAdapter增强逻辑在于重新加载新的class方法,如果调用的是新增的构造方法,则调用Hotcode2中为这个新方法提供的映射接口;调用被删除的构造方法,则抛出HotCodeException;余下的按照正常的流程。

//参考实现类NewInstanceModifier
//转译为java代码如下
    JdkReflectHelper.checkReload(this.clazz);
    if (JdkConstructorReflectHelper.isNewConstructorAccess(this)) {
      return JdkConstructorReflectHelper.newConstructorNewInstance(this, paramVarArgs);
    }
//继续java.lang.reflect.Constructor.newInstance代码流程    
    if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
    ......
    return (T) ca.newInstance(initargs);            

对于Annotation的处理,主要将新增的Annotation映射到当前class在Hotcode2 runtime对应的shadowClass中。

列位,这里出现了一个新的名词shadowClass,这个词涉及到Hotcode2如何维护jdk Class的实例对象,如何响应java application的方法调用、字段访问。详细信息请参考《Hotcode2原理》(待写O(∩_∩)O~)

1.7 其余Adapter

ResourceBundleControlAdapter/
PropertyResourceBundleAdapter 这2个主要是对properties资源文件的处理。

2 Runtime阶段

用在Runtime阶段的Adapter主要是用于处理应用的class文件。 com.taobao.hotcode2.code.ClassTransformer.transformReloadClass/transformNewLoadClass/transformJdkDynamicProxyClass/transformAnnotationProxyClass 这4个方法通过各种Adapter对应用class文件进行处理。比较重要的:

  • AddFieldsHolderAdapter 会对目标类新增2个FieldsHolder类型的变量。
  • AddMethodRouterAdapter 会对目标类新增4个路由方法,
 public Object __hotcode_instance_method_router__(int paramInt, Object[] paramArrayOfObject);
 public Object __hotcode_package_instance_method_router__包名(int paramInt, Object[] paramArrayOfObject);
 public Object __hotcode_private_instance_method_router__包名$类名(int paramInt, Object[] paramArrayOfObject);
 public static Object __hotcode_static_method_router__包名$类名(int paramInt, Object[] paramArrayOfObject)
  • 用于生成辅助类:"类名+$$S$+序号"中方法的ClearMethodBodyAdapter,这个Adapter不会生成方法体
  • MethodBodyTransformAdapter,将例如invoker.run() -->greet.hello(),实际上greet.hello()的调用被替换为greet.__hotcode_instance_method_router__(int,Object[])

其余还有些,每个Adapter代码比较少,容易理解。

  • JdkDynamicProxyAdapter
  • AnnotationProxyAdapter
  • StaticFieldDefaultValueAdapter
  • BeforeMethodCheckAdapter
  • AddClassReloaderAdapter
  • NonGeneratedClassMethodBodyTransformerAdapter
  • FieldTransformAdapter
  • AddInheritedMethodAdapter
  • AnonymousInnerClassAdapter

3 总结

如果Spring是面向Bean的编程,iBatis是基于Statement,那么Hotcode2则是由各个Adapter串联起来的!

4 后记

差不多用了一个月多,才断断续续将这3篇文章,其中不停的查资料、骚扰@千臂(很感谢,O(∩_∩)O~),发现自己了解的越多,还需要继续了解的知识点就更多。。。总之,学习是一种信仰!

目录
相关文章
|
3月前
|
算法 Java
太狠了!阿里技术专家撰写的电子版JVM&G1 GC实战,颠覆了传统认知
JVM是Java语言可以跨平台、保持高发展的根本,没有了 JVM, Java语言将失去运行环境。针对 Java 程序的性能优化一定不可能避免针对JVM 的调优,随着 JVM 的不断发展,我们的应对措施也在不断地跟随、变化,内存的使用逐渐变得越来越复杂。所有高级语言都需要垃圾回收机制的保护,所以 GC 就是这么重要。
|
21天前
|
缓存 Java C#
【JVM故障问题排查心得】「Java技术体系方向」Java虚拟机内存优化之虚拟机参数调优原理介绍(一)
【JVM故障问题排查心得】「Java技术体系方向」Java虚拟机内存优化之虚拟机参数调优原理介绍
60 0
|
1月前
|
Cloud Native Java Docker
【Spring云原生】Spring官宣,干掉原生JVM,推出 Spring Native!整体提升性能!Native镜像技术在Spring中的应用
【Spring云原生】Spring官宣,干掉原生JVM,推出 Spring Native!整体提升性能!Native镜像技术在Spring中的应用
|
2月前
|
存储 监控 Java
JVM监控和分析技术在实践中可能会面临什么?
JVM监控和分析技术在实践中可能会面临什么?
|
2月前
|
存储 监控 数据可视化
技术小白应该知道的关于JVM的一些事
技术小白应该知道的关于JVM的一些事
|
5月前
|
Java API Apache
深入理解JVM系列教程(10) - 字节码技术
深入理解JVM系列教程(10) - 字节码技术
43 1
|
6月前
|
XML 设计模式 JavaScript
JVM第三讲:JVM 基础-字节码的增强技术详解
JVM第三讲:JVM 基础-字节码的增强技术详解
JVM第三讲:JVM 基础-字节码的增强技术详解
|
8月前
|
存储 缓存 架构师
京东架构师呕心整理:jvm与性能调优有哪些核心技术知识点
相信很多人对于性能调优都不陌生,为了获得更好的系统性能,或者是为了满足不断增加的业务需求。都需要用到我们的性能调优。所以性能优化在面试中出现的频率特别高,这篇文章我主要给大家整理了大厂里面关于jvm和性能调优用到的一些核心技术知识点。
58 0
|
Oracle JavaScript Cloud Native
【JVM深层系列】「云原生时代的Java虚拟机」针对于GraalVM的技术知识脉络的重塑和探究
【JVM深层系列】「云原生时代的Java虚拟机」针对于GraalVM的技术知识脉络的重塑和探究
246 1
【JVM深层系列】「云原生时代的Java虚拟机」针对于GraalVM的技术知识脉络的重塑和探究
|
存储 前端开发 rax
「技术翻译」JVM研究系列「绝版敲门砖」带你进入JVM-ZGC垃圾回收器的时代和未来
「技术翻译」JVM研究系列「绝版敲门砖」带你进入JVM-ZGC垃圾回收器的时代和未来
133 0