当前位置: 首页 > 科技观察

Arthas用得好,写个Lambda表达式就能跪?我应该怎么办?

时间:2023-03-15 22:05:49 科技观察

作为一个从PHP转Java的人,发现阿里巴巴的arthas很好用。通过arthas的redefine命令,和PHP一样,可以在不重新发布的情况下改变程序的行为(前提是类结构和方法签名没有改变)。但是用多了之后发现,很多时候,改了几行代码,有时甚至加一行日志,就无法重新定义。提示redefineerror!java.lang.UnsupportedOperationException:classredefinitionfailed:attemptedtoaddamethod提示我们添加新的方法,那我们看看有没有添加新的方法。通过javap查看定义的方法:Thisistheoldclass:Thisisnewclass:经过对比发现是新类,也就是本地编译的类,lambdas对应的方法名是lambda$getAllCity$0,最后一个数字从0开始。在老类中,也就是现在运行的类中,同一个lambda的方法名是lambda$getAllCity$121,最后一个数字是一个非常大的数字。仔细对比后发现是jdk版本问题,不同jdk版本的处理可能lamdba不一致。具体来说,网上编译的jdk版本是1.8.0_66-b17,而本地版本是1.8.0_222-b10,这两个版本的lambda对应的方法名是不一样的。首先,为了调试方便,写一个最新的小复用例子来看看://Compile.java//编译LamdbaTest1.java和LamdbaTest2.javaimportjavax.tools.*;importjava.io.File;publicclassCompile{publicstatic[voidmain(s)){Stringpath1="/path/to/LamdbaTest1.java";Stringpath2="/path/to/LamdbaTest2.java";JavaCompilerjavaCompiler=ToolProvider.getSystemJavaCompiler();DiagnosticCollectordiagnostics=newDiagnosticCollector();StandardJavaFileManagerfileManager=javaCompiler.getStandardFileManager(诊断,null,null);IterablecompilationUnits=fileManager.getJavaFileObjects(newFile(path1),newFile(path2));JavaCompiler.CompilationTasktask=javaCompiler.getTask(null,fileManager,diagnostics,null,null,compilationUnits);booleansuccess=task.call();System.out.println(success);}}//LamdbaTest1.javapublicclassLamdbaTest1{privatevoidtest(Runnablerunnable){runnable.run();}privatevoidmain()throwsThrowable{test(()->{System.out.println(11);});}}//LamdbaTest2.javapublicclassLamdbaTest2{privatevoidtest(Runnablerunnable){runnable.run();}privatevoidmain()throwsThrowable{test(()->{System.out.println(22);});}}使用1.8.0_222-b10(新版jdk)运行后发现LambdaTest2中的lambda方法为:privatestaticvoidlambda$main$0();更改版本1.8.0_66-b17(旧版本jdk)后,lambda方法变为:privatestaticvoidlambda$main$1();尝试同时编译几个文件,我们可以发现:对于老版本的javac,末尾的数字是全局递增的,50个类有100个lambda,最后一个lambda的个数是99;而新版本是每节课的重新统计与总课时数无关。确认问题后,接下来就是不断中断重试。后来发现javac不同版本的逻辑确实不一样。首先,通过查看jdk源码可以知道lambda的方法名是:lambda$$不同的是新版本的javac在处理新的时候会保存之前的lambdaCount班级。然后恢复,在当前类中,会直接从0开始计数:老版本没有这个逻辑,直接使用全局递增的计数器:这说明老版本的编译器确实是通过lambda全局编号的.那么,问题来了,这个行为是从哪个版本改变的?对比后发现这个变化是jdk8u74-b02引入的。对应的bug是https://bugs.openjdk.java.net/browse/JDK-8067422基本上每个类中的lambda都是单独编号的,保证编译顺序不会影响lambda的方法名。所以解决方法很简单,升级编译环境的jdk版本即可。无独有偶,前两天为了更好的适应Docker运行环境(通俗的说就是获取容器中docker的cpu配额,而不是物理机的cpu个数),问了下运维添加新的jdk版本为1.8.0_231-b11,所以只需要直接将编译环境的jdk版本切换为8u231即可!Arthas目前正在征集论文。如果你有使用Arthas查过的问题,解读Arthas源码并对ArthasUnlimited提出建议,其他与Arthas相关的内容