漫谈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~),发现自己了解的越多,还需要继续了解的知识点就更多。。。总之,学习是一种信仰!

目录
相关文章
|
6月前
|
算法 Java
太狠了!阿里技术专家撰写的电子版JVM&G1 GC实战,颠覆了传统认知
JVM是Java语言可以跨平台、保持高发展的根本,没有了 JVM, Java语言将失去运行环境。针对 Java 程序的性能优化一定不可能避免针对JVM 的调优,随着 JVM 的不断发展,我们的应对措施也在不断地跟随、变化,内存的使用逐渐变得越来越复杂。所有高级语言都需要垃圾回收机制的保护,所以 GC 就是这么重要。
|
6月前
|
监控 Oracle Java
《深入浅出Java虚拟机 — JVM原理与实战》带你攻克技术盲区,探索各大JVM虚拟机特色 —— JVM故障排除指南(先导篇)
《深入浅出Java虚拟机 — JVM原理与实战》带你攻克技术盲区,探索各大JVM虚拟机特色 —— JVM故障排除指南(先导篇)
105 0
|
5月前
|
监控 Java 调度
探秘Java虚拟机(JVM)性能调优:技术要点与实战策略
【6月更文挑战第30天】**探索JVM性能调优:**关注堆内存配置(Xms, Xmx, XX:NewRatio, XX:SurvivorRatio),选择适合的垃圾收集器(如Parallel, CMS, G1),利用jstat, jmap等工具诊断,解决Full GC问题,实战中结合MAT分析内存泄露。调优是平衡内存占用、延迟和吞吐量的艺术,借助VisualVM等工具提升系统在高负载下的稳定性与效率。
100 1
|
26天前
|
安全 Java API
🌟探索Java宇宙:深入理解Java技术体系与JVM的奥秘
本文深入探讨了Java技术体系的全貌,从Java语言的概述到其优点,再到Java技术体系的构成,以及JVM的角色。旨在帮助Java开发者全面了解Java生态,提升对Java技术的认知,从而在编程实践中更好地发挥Java的优势。关键词:Java, JVM, 技术体系, 编程语言, 跨平台, 内存管理。
29 2
|
6月前
|
缓存 算法 安全
【JVM故障问题排查心得】「Java技术体系方向」Java虚拟机内存优化之虚拟机参数调优原理介绍(二)
【JVM故障问题排查心得】「Java技术体系方向」Java虚拟机内存优化之虚拟机参数调优原理介绍
63 0
|
6月前
|
缓存 Java C#
【JVM故障问题排查心得】「Java技术体系方向」Java虚拟机内存优化之虚拟机参数调优原理介绍(一)
【JVM故障问题排查心得】「Java技术体系方向」Java虚拟机内存优化之虚拟机参数调优原理介绍
152 0
|
5月前
|
存储 算法 Java
技术笔记:JVM的垃圾回收机制总结(垃圾收集、回收算法、垃圾回收器)
技术笔记:JVM的垃圾回收机制总结(垃圾收集、回收算法、垃圾回收器)
57 1
|
4月前
|
监控 Java 调度
探索JVM性能调优,调优不仅是技术挑战,更是成长过程。
【7月更文挑战第1天】探索JVM性能调优:** 本文深入JVM内存模型,关注堆内存与方法区、栈的优化,通过调整-Xms, -Xmx及垃圾收集器参数减少GC频率。探讨了Serial到G1等垃圾收集器的选择策略,利用jstat、jmap等工具诊断性能瓶颈。实战案例中,通过问题定位、内存分析解决Full GC问题,强调开发者需理解JVM原理,运用工具在复杂场景下实现高效调优。调优不仅是技术挑战,更是成长过程。
44 0
|
5月前
|
存储 缓存 算法
详解JVM内存优化技术:压缩指针
详解JVM内存优化技术:压缩指针
|
5月前
|
存储 缓存 监控
深入解析JVM内存分配优化技术:TLAB
深入解析JVM内存分配优化技术:TLAB