所以我有一个类加载器(MyClassLoader),它在内存中维护一组"特殊"类.这些特殊类是动态编译的,并存储在MyClassLoader内的字节数组中.当MyClassLoader被要求提供类时,它首先检查它的specialClasses字典是否包含它,然后再委托给System类加载器.它看起来像这样:
class MyClassLoader extends ClassLoader {
Map<String, byte[]> specialClasses;
public MyClassLoader(Map<String, byte[]> sb) {
this.specialClasses = sb;
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (specialClasses.containsKey(name)) return findClass(name);
else return super.loadClass(name);
}
@Override
public Class findClass(String name) {
byte[] b = specialClasses.get(name);
return defineClass(name, b, 0, b.length);
}
}
Run Code Online (Sandbox Code Playgroud)
如果我想在其上执行转换(例如检测)specialClasses,我可以简单地通过修改byte[]之前调用defineClass()它来实现.
我还想转换由System类加载器提供的类,但是System类加载器似乎没有提供任何方式来访问byte[]它提供的类的原始类,并Class直接给我提供了对象.
我可以使用一个-javaagent仪器加载到JVM中的所有类,但这会增加我不想检测的类的开销; 我只是想要MyClassLoader加载的类进行检测.
byte[]父类加载器提供的类的原始,所以我可以在定义自己的副本之前检测它们?byte[],以便MyClassLoader可以检测和定义自己的所有System类(Object,String等)的副本?编辑:
所以我尝试了另一种方法:
-javaagent,捕获 …AFAIK,Java中的隐式构造函数总是为没有构造函数的类生成[1],[2].
但在字节码中,我无法在JVMS上找到这样的限制.
所以:
根据JVMS来定义一个没有构造函数的类只有在下面的jasmin hello世界中使用它的静态方法是有效的吗?
除了无法创建它的实例之外还有其他任何后果吗?我将无法使用invokespecial初始化实例,new根据https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.10.2.4,这些实例无效(可以不使用未初始化的对象.
Jasmin代码:
.class public Main
.super java/lang/Object
.method public static main([Ljava/lang/String;)V
.limit stack 2
getstatic java/lang/System/out Ljava/io/PrintStream;
ldc "Hello World!"
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
return
.end method
Run Code Online (Sandbox Code Playgroud)
也就是说,没有构造函数:
.method public <init>()V
aload_0
invokenonvirtual java/lang/Object/<init>()V
return
.end method
Run Code Online (Sandbox Code Playgroud)
?
运行与java Main给出预期的输出Hello World!.
我检查了javap -v输出,与Java不同,jasmin没有生成默认构造函数.
我还试着打电话new Main();来看看会发生什么:
public class TestMain {
public static void main(String[] …Run Code Online (Sandbox Code Playgroud) 使用Asm可以很容易地定义简单的getter和setter(幸运的是,它甚至可以在FAQ中解释).但是有一件事没有提到,而且我一直无法找到文档,那就是如何使用泛型类型信息来实现它们.
我实际上能够很容易地确定泛型类型信息(因为代码将采用现有的字段和/或方法,并且存在完整的泛型类型处理和解析).我只需要为包含泛型类型的类型生成泛型版本.
我希望这就像修改签名的Asm ClassWriter/MethodVisitor调用一样容易,但文档中的一些注释表明它可能不那么容易(因为泛型信息存储在与常规信息不同的位置).
编辑:看起来入口点是"ClassWriter.visitField/Method(....,String signature) - 请注意,它的"描述"包含正常的非泛型类信息,但术语"签名"(在JLS中)具体指的是到泛型 - 包括类型信息.
据我所知,Java编译为Java字节码,然后可以由任何运行Java的机器解释其特定的CPU.Java使用JIT来解释字节码,我知道这样做的速度非常快,但为什么语言设计者一旦检测到正在运行的特定机器,就不会静态编译成机器指令?是否每次通过代码解释字节码?
目前我只是在一个使用java字节码的项目中.我经常看到,当创建一个新的类实例并在其上调用一个方法时,字节码将是:
NEW <MyClass>
DUP
INVOKESPECIAL <MyClass.<init>>
Run Code Online (Sandbox Code Playgroud)
这里为什么要做"DUP"?从VM Spec,我得到描述"在操作数堆栈上复制顶部值并将重复的值推送到操作数堆栈".但是为什么在这里需要复制操作数堆栈的最高值呢?谢谢.
我如何从Python AST生成.pyc文件,以便我可以从Python导入文件?
我曾经compile创建过一个代码对象,然后将co_code属性写入文件,但是当我尝试从Python导入文件时,我得到了一个ImportError: Bad magic number in output.pyc.
我想知道scala如何生成字节代码,它是否使用像ASM这样的库?或者只是将二进制文件写入.class文件以获得性能?
我刚刚从Java代码中使用Scala代码生成的字节码时,我发现Scala范围很奇怪.请考虑使用Spark(Spark 1.4,Hadoop 2.6)的以下代码段:
import java.util.Arrays;
import java.util.List;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.broadcast.Broadcast;
public class Test {
public static void main(String[] args) {
JavaSparkContext sc =
new JavaSparkContext(new SparkConf()
.setMaster("local[*]")
.setAppName("test"));
Broadcast<List<Integer>> broadcast = sc.broadcast(Arrays.asList(1, 2, 3));
broadcast.destroy(true);
// fails with java.io.IOException: org.apache.spark.SparkException:
// Attempted to use Broadcast(0) after it was destroyed
sc.parallelize(Arrays.asList("task1", "task2"), 2)
.foreach(x -> System.out.println(broadcast.getValue()));
}
}
Run Code Online (Sandbox Code Playgroud)
这段代码失败了,这是因为我Broadcast在使用它之前自愿销毁它,但问题是在我的心理模型中它甚至不应该编译,更不用说运行正常了.
事实上,Broadcast.destroy(Boolean)声明是private[spark]这样,它不应该从我的代码可见.我会尝试查看字节码,Broadcast但这不是我的专长,这就是我更喜欢发布这个问题的原因.另外,抱歉,我太懒了,不能创建一个不依赖Spark的例子,但至少你明白了.请注意,我可以使用Spark的各种package-private方法,它不仅仅是Broadcast.
知道发生了什么事吗?
当我得到一个NPE时,我会得到一个带行号的堆栈跟踪.这很有用,但是如果行非常密集和/或包含嵌套表达式,那么仍然无法确定哪个引用为null.
当然,这些信息一定在某处可用.有没有办法解决这个问题?(如果不是java表达式,那么至少导致NPE的字节码指令也会有帮助)
编辑#1:我看过一些评论暗示分手等等,没有任何冒犯,实际上是非建设性和无关紧要的.如果我能做到这一点,我会的!我只想说修改源是不可能的.
编辑#2:apangin在下面发布了一个很好的答案,我接受了.但是,对于那些不想自己尝试的人来说,我必须将输出包含在这里!;)
假设我有这个驱动程序TestNPE.java
1 public class TestNPE {
2 public static void main(String[] args) {
3 int n = 0;
4 String st = null;
5
6 System.out.println("about to throw NPE");
7 if (n >= 0 && st.isEmpty()){
8 System.out.println("empty");
9 }
10 else {
11 System.out.println("othereise");
12 }
13 }
14
15 }
Run Code Online (Sandbox Code Playgroud)
字节码看起来像这样(仅显示main()方法并省略其他不相关的部分)
Code:
stack=2, locals=3, args_size=1
0: iconst_0
1: istore_1
2: aconst_null
3: astore_2
4: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #3 // String …Run Code Online (Sandbox Code Playgroud) bytecode ×10
java ×7
jvm ×3
scala ×2
apache-spark ×1
class ×1
classloader ×1
constructor ×1
debugging ×1
generics ×1
interpreter ×1
jit ×1
python ×1
ruby ×1
stack-trace ×1