Hotswap code with lambda 失败,说明方法无法删除

Hotswap code with lambda fails, indicating that the method cannot be deleted

问题

热交换 lambda 失败。

进程

我尝试热交换包含 lambda 的方法体。

1。热插拔代码

public class Example {
    private int x;
    public void print (int y) {
       Consumer<Integer> consumer = (key) -> System.out.println(key);
    }
}

2。一个例子

 //Methods after sugar removal
First use code 1 to compile a class and put it in the directory, then run code 2 hot change
        //from static to instance method failed
        1、Consumer<Integer> consumer = (key) -> System.out.println(key);     
        2、Consumer<Integer> hot = (key) -> System.out.println(key + "" + this);
        //After desugar method
       private static synthetic lambda$print[=11=](Ljava/lang/Integer;)V
       private synthetic lambda$print(Ljava/lang/Integer;)V

        //change static method params count successful
        String str = "args";
        Consumer<Integer> methodCount1 = (key) -> System.out.println(key);
        Consumer<Integer> methodCount2 = (key) -> System.out.println(key + str);
        private static synthetic lambda$print(Ljava/lang/Integer;)V
        private static synthetic lambda$print(Ljava/lang/String;Ljava/lang/Integer;)V

        //change instance method params count failed
        Consumer<Integer> instanceCount2 = (key) -> System.out.println(key + "" + this);
        Consumer<Integer> instanceCount3 = (key) -> System.out.println(key + str + this);
        private synthetic lambda$print(Ljava/lang/Integer;)V
        private synthetic lambda$print(Ljava/lang/String;Ljava/lang/Integer;)V


        //from nothing add or delete lambd  successful (compile static method)

        //from nothing add or delete lambd  fail (compile instance method)
        Consumer<Integer> instaceMethod = (key) -> System.out.println(this);
        private synthetic lambda$print(Ljava/lang/Integer;)V
        hot.accept(1);
        consumer.accept(1);

3。热插拔类型代码

代理代码
   public class JavaAgent {

        public static void agentmain(String agentArgs, Instrumentation inst) throws Exception {
            System.out.println("开始热更新");
            // 获得所有已被装载的class文件
            Class[] classes = inst.getAllLoadedClasses();
            for (Class clazz : classes) {
                if (clazz.getName().equalsIgnoreCase(agentArgs) || clazz.getName().equalsIgnoreCase("com.jason.Normal")) {
                    System.out.println(clazz.getName());
                    inst.addTransformer(new ClassFileTransformer() {

                        @Override
                        public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {

                                //System.out.println("hotswap class name :" + className);
                                String str = "";
                                if (className.contains("Example")){
                                    str = "Example.class";
                                }else if (className.contains("Norm")){
                                    str = "Normal.class";
                                }else {
                                    return null;
                                }
                                byte[] bytes = fileToBytes(new File("C:\hot\"+str));
                                return bytes;
                        }
                    }, true);
                    // 重转换
                    inst.retransformClasses(clazz);
                }
            }
            System.out.println("热更新结束");
        }

        public static byte[] fileToBytes(File file) {
            try {
                FileInputStream in = new FileInputStream(file);
                byte[] bytes = new byte[in.available()];
                in.read(bytes);
                in.close();
                return bytes;
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
     }
热插拔代码
     public class HotSwap {
         public static void hot(){
                try {
                    List<VirtualMachineDescriptor> list = VirtualMachine.list();
                    for (VirtualMachineDescriptor vmd : list) {
                            VirtualMachine virtualMachine = null;
                            virtualMachine = VirtualMachine.attach(vmd.id());
                            // 获得代理类位置 + 传递参数
                            virtualMachine.loadAgent("C:\Users\DELL\Downloads\JavaHotSwap-master\JavaAgent\target\agentmain.jar", "com.jason.Example");
                            virtualMachine.detach();
                    }
                } catch (AttachNotSupportedException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (AgentLoadException e) {
                    e.printStackTrace();
                } catch (AgentInitializationException e) {
                    e.printStackTrace();
                }
            }  
}
主要代码
int i = new Random().nextInt();
        new Example().print(i);
       // Normal.show();
       // System.out.println("hot");
        HotSwap.hot();
        new Example().print(i);
       // Normal.show();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
例外
java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:386)
    at sun.instrument.InstrumentationImpl.loadClassAndCallAgentmain(InstrumentationImpl.java:411)
Caused by: java.lang.UnsupportedOperationException: class redefinition failed: attempted to add a method
    at sun.instrument.InstrumentationImpl.retransformClasses0(Native Method)
    at sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:144)
    at com.jason.JavaAgent.agentmain(JavaAgent.java:60)
    ... 6 more
com.sun.tools.attach.AgentInitializationException: Agent JAR loaded but agent failed to initialize
    at sun.tools.attach.HotSpotVirtualMachine.loadAgent(HotSpotVirtualMachine.java:121)
    at com.jason.HotSwap.hot(HotSwap.java:23)
    at com.jason.Main.main(Main.java:33)
Agent failed to start!
Exception in thread "Attach Listener" 

3。想法

我知道编译字节码的时候,仪器是不能改变(增删改查)方法的。 为什么我可以随意修改静态方法,却不能修改实例方法?

你无意中遇到了 JDK-8192936。根据错误报告,围绕 Java 6 所做的更改使实现接受有关添加或删除 private staticprivate final 方法的更改,尽管规范继续声明这不是支持。

因此,当将 lambda 表达式脱糖为 private static 方法时,它适用于所有未捕获 this 的 lambda 表达式,更改它们恰好有效。

对于捕获 this 的 lambda 表达式,行为取决于编译器。将它们脱糖到没有 final 修饰符的 private 实例方法中已经变得很普遍,因此,它们不受报告行为的影响。但原则上,编译器可以添加冗余 final 修饰符。也可以将这种 lambda 表达式的主体转换为 static 方法,接受 this 引用作为参数。旧编译器会发生这种情况。

根据报告的修复不是考虑规范中“这些限制可能会在未来版本”的声明,而是删除功能以匹配字面上的规范。将有一个选项 -XX:{+|-}AllowRedefinitionToAddOrDeleteMethods 来切换以 JDK 13 开头的行为一段时间。