我记得一位教授曾经说过,解释后的代码比编译的代码慢了10倍.解释和字节码之间的速度差异是多少?(假设字节码不是JIT编译的)
我问,因为有些人一直在围绕将vim脚本编译成字节码的想法,我只是想知道会得到什么样的性能提升.
我正在使用asm库来执行一些Java字节码修改 - 专门用于修改我的类以实现新的接口和相关方法.我目前的方法是通过javaagent使用核心asm API.我想保留这种动态方法,而不是静态修改.class文件.
在更高的层次上,我的问题是,如果我选择修改从B扩展的A类,我还需要修改B.(鉴于我对如何在JVM中加载类的理解,我相信B类将永远是在A级之前交给变压器.(如果我错了,请纠正我.)鉴于这个假设,我认为我需要返回并重新转换 B.我的方法在这段代码中被捕获:
public byte[] transform(ClassLoader l, String name, Class<?> clazz, ProtectionDomain d, byte[] b) {
throws IllegalClassFormatException {
// **1**
System.out.println("--->>> " + name);
if (interestingClass(name)) {
try {
ClassReader cr = new ClassReader(b);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
PyClassVisitorAdapter pv = new PyClassVisitorAdapter(cw, name);
cr.accept(pv, 0);
// **2** Retrieve the superclass and try to transform that
if (! "Ljava/lang/Object;".equals(pv.getSuperName())) {
String cName = classJvmToCanonical(pv.getSuperName());
Class[] classes = inst.getAllLoadedClasses();
for (Class c …Run Code Online (Sandbox Code Playgroud) 我正在写一个Bytecode指导员.现在,我试图找出在物体存在的情况下如何做到这一点.我想对我在JVMS中阅读的两行(第4.9.4节)做一些澄清:
1)"验证者在初始化之前拒绝使用新对象的代码."
我的问题是,"使用"在这里意味着什么?我猜它的意思是:把它当作一个方法属性,调用GETFIELD和PUTFIELD它,或调用它的任何实例方法.他们的其他禁止用途是什么?而且我相信它遵循其他指令,例如DUP,LOAD并且STORE被允许.
2)"在该方法调用myClass的另一个实例初始化方法或其直接超类之前,该方法可以对此执行的唯一操作是分配在myClass中声明的字段."
这意味着在一个<init>方法中,GETFIELD和PUTFIELD在<init>被调用之前是允许的.但是,在Java中,在调用之前对实例字段执行任何操作super()或this()导致编译错误.有人可以澄清一下吗?
3)我还有一个问题.对象引用何时初始化,因此可以自由使用?通过阅读JVMS,我想出了一个对象是否被初始化的答案,取决于每个方法.在某个时间点,可以为方法初始化对象,但不能为另一个方法初始化对象.具体来说,当<init>该方法返回时,对象的方法被初始化.
例如,考虑该main()方法创建了一个对象并调用<init>它然后调用超类<init>.从返回后super(),该对象现在被视为初始化<init>,但尚未初始化main().这是否意味着,在<init>之后super(),我可以将对象作为参数传递给方法,甚至在返回main()之前.
有人可以证实这整个分析是真的吗?感谢您的时间.
ps:我实际上已经向Sun论坛发布了同样的问题但是回复了.我希望我能在这里有更多的运气.谢谢.
更新
首先感谢您的回答和时间.虽然我没有得到一个明确的答案(我有很多问题,其中一些有点模糊),但你的答案和例子,以及随后的实验,对于我更深入地了解JVM如何工作非常有用.
我发现的主要问题是Verifier的行为因不同的实现和版本而不同(这使得字节码操作的工作变得更加复杂).问题在于不符合JVMS,或者验证者的开发人员缺乏文档,或者JVMS在验证者的区域中有一些微妙的模糊性.
最后一件事,SO Rocks !!! 我在官方的Sun JVM规范论坛上发布了同样的问题,直到现在我仍然没有回答.
我想编写一个MethodVisitor来转换用于乘法的LDC指令.
字节码示例:
ldc #26
imul
Run Code Online (Sandbox Code Playgroud)
这基本上推动了一个常数然后乘以它.
它必须是有状态的转换,因为我首先必须检查它是否为乘法,如果是,我需要返回到ldc指令并修改常量.我不完全确定我会怎么做,我不知道如何修改常量(当我尝试传递一个不同的值时,旧的值仍然保留在常量池中).
编辑:
public class AdditionTransformer extends MethodAdapter {
boolean replace = false;
int operand = 0;
AdditionTransformer(MethodVisitor mv) {
super(mv);
}
@Override
public void visitInsn(int opcode) {
if (opcode == IMUL && replace) {
operand *= 2;
visitLdcInsn(operand);
replace = false;
}
mv.visitInsn(opcode);
}
@Override
public void visitLdcInsn(Object cst) {
if (cst instanceof Integer && !replace) {
operand = (Integer) cst;
replace = true;
} else {
mv.visitLdcInsn(cst);
}
}
}
Run Code Online (Sandbox Code Playgroud)
这就是我所拥有的,但它不会删除常量池中的旧值,并且可能存在错误.
我使用ProGuard将一些jar文件从类文件版本49.0"升级"到50.0.
因为预验证对于版本50.0是可选的,我想知道是否有办法检查类文件中是否真的有StackMapTable属性.
(并不是我不相信ProGuard"[...]以下选项通过更新内部版本号并预先验证它们来将类文件升级到Java 6."但我想学习如何验证它的存在性StackMapTable.)
我正在为LLVM中的特殊用途语言编写编译器.我想为已经用C++编写的库添加绑定.我的想法是将库编译为LLVM字节码(使用clang -emit-llvm -S abc.c)并在编译期间将其链接.这适用于类似的代码
// lib.c
int f() {
return 123;
}
Run Code Online (Sandbox Code Playgroud)
但是库的一部分写得像
// A.cc
class A {
public:
int f() { return 123; }
};
Run Code Online (Sandbox Code Playgroud)
这导致空字节码文件.我知道我可以通过分离实现来解决这个问题:
// A.cc
class A {
public:
int f();
};
int A::f() {
return 123;
}
Run Code Online (Sandbox Code Playgroud)
但这将是一项繁琐的工作.有没有办法从我的库源创建有用的字节码呢?或者在我的编译器中使库可用的任何其他方法?
执行Java REST服务性能测试我看到一个意外的模式:在每次调用中创建并返回相同值对象的方法比仅返回存储在类或对象字段中的值对象的另一个版本运行得更快.
码:
@POST @Path("inline") public Response inline(String s) {
return Response.status(Status.CREATED).build();
}
private static final Response RESP = Response.status(Status.CREATED).build();
@POST @Path("staticfield") public Response static(String s) {
return RESP;
}
private final Response resp = Response.status(Status.CREATED).build();
@POST @Path("field") public Response field(String s) {
return resp;
}
Run Code Online (Sandbox Code Playgroud)
字节代码:
性能(使用Apache AB,单线程,多次运行,结果一致):
环境:RHEL6 + JDK Oracle 1.7.0_60-b19 64位
有可能JVM使用本机代码优化了内联版本,但从未考虑优化其他两个,因为它们已经很小了吗?
我正在尝试将Cglib代理转换为ByteBuddy.Cglib有net.sf.cglib.proxy.Proxy接口来拦截所有方法调用.我检查了ByteBuddy的文档但是找不到这样的例子.如果我没有用ByteBuddy实例化的每个对象都有这样的接口,我会再次重复同样的事情并且agin.使用ByteBuddy有更好的方法吗?
这是我的示例代码段:
服务:
public class MyService {
public void sayFoo() {
System.out.println("foo");
}
public void sayBar() {
System.out.println("bar");
}
}
Run Code Online (Sandbox Code Playgroud)
拦截器:
public class MyServiceInterceptor {
public void sayFoo(@SuperCall Callable<Void> zuper) {
try {
zuper.call();
} catch (Exception e) {
e.printStackTrace();
}
}
public void sayBar(@SuperCall Callable<Void> zuper) {
try {
zuper.call();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Run Code Online (Sandbox Code Playgroud)
测试:
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.ClassFileVersion;
import net.bytebuddy.dynamic.ClassLoadingStrategy;
import net.bytebuddy.instrumentation.MethodDelegation;
import net.bytebuddy.instrumentation.method.matcher.MethodMatchers;
public class Main {
public static void …Run Code Online (Sandbox Code Playgroud) bytecode ×10
java ×8
jvm ×2
performance ×2
verification ×2
android ×1
byte-buddy ×1
c++ ×1
clang ×1
class ×1
dalvik ×1
dex ×1
interpreter ×1
jvm-hotspot ×1
llvm ×1
object ×1
reflection ×1
scala ×1
vim ×1