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 static
和 private final
方法的更改,尽管规范继续声明这不是支持。
因此,当将 lambda 表达式脱糖为 private static
方法时,它适用于所有未捕获 this
的 lambda 表达式,更改它们恰好有效。
对于捕获 this
的 lambda 表达式,行为取决于编译器。将它们脱糖到没有 final
修饰符的 private
实例方法中已经变得很普遍,因此,它们不受报告行为的影响。但原则上,编译器可以添加冗余 final
修饰符。也可以将这种 lambda 表达式的主体转换为 static
方法,接受 this
引用作为参数。旧编译器会发生这种情况。
根据报告的修复不是考虑规范中“这些限制可能会在未来版本”的声明,而是删除功能以匹配字面上的规范。将有一个选项 -XX:{+|-}AllowRedefinitionToAddOrDeleteMethods
来切换以 JDK 13 开头的行为一段时间。
问题
热交换 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 static
和 private final
方法的更改,尽管规范继续声明这不是支持。
因此,当将 lambda 表达式脱糖为 private static
方法时,它适用于所有未捕获 this
的 lambda 表达式,更改它们恰好有效。
对于捕获 this
的 lambda 表达式,行为取决于编译器。将它们脱糖到没有 final
修饰符的 private
实例方法中已经变得很普遍,因此,它们不受报告行为的影响。但原则上,编译器可以添加冗余 final
修饰符。也可以将这种 lambda 表达式的主体转换为 static
方法,接受 this
引用作为参数。旧编译器会发生这种情况。
根据报告的修复不是考虑规范中“这些限制可能会在未来版本”的声明,而是删除功能以匹配字面上的规范。将有一个选项 -XX:{+|-}AllowRedefinitionToAddOrDeleteMethods
来切换以 JDK 13 开头的行为一段时间。