- 浏览: 50620 次
- 性别:
- 来自: 成都
最新评论
-
yaoyqsz:
connector.setPort(9999); ----& ...
jetty的使用 -
长夜慢慢:
很有帮助,辛苦了!!!
java web.xml listener filter servlet
JVM知识点题目
JVM是Java程序的运行环境,因此对于JVM的掌握有助于理解Java程序的执行以及编写,尤其是运行时碰到的一些诡异问题,那么怎么样能考察自己对于JVM关键知识点的掌握情况,帮助学习JVM机制呢,在这篇blog中来探讨下。
对于Java程序而言,JVM的关键机制有:字节码的加载、方法的执行、对象内存的分配和回收、线程和锁机制,这几个机制涉及到的jvm的知识点远没有写这几个字这么简单,里面的复杂度还是非常高的。
字节码的加载
JVM通过ClassLoader来完成字节码的动态加载,这里面涉及到的主要是ClassLoader的双亲委派、ClassLoader的编写方法、Class是否被加载的唯一标识以及Class的加载过程。
在考察的时候我觉得可以以这么两道简单的题来考察:
1、写一段将目录中指定的.class文件加载到JVM的程序,并通过Class对象获取到完整类名等信息;
2、一段展示代码,里面包含一个全局静态整型变量,问如果用两个ClassLoader加载此对象,执行这个整型变量++操作后结果会是怎么样的?
方法的执行
JVM有自己的一套指令系统,字节码中即已经是指令了,需要大概掌握了JVM对static、interface、instance、构造器采用的不同的执行方法,另外就是JVM中反射的实现(可以以Sun JDK来举例)、动态代理的实现,最后相关的就是JVM执行字节码的方式(解释、JIT、Hotspot),以及什么时候触发编译成机器码,如何控制。
在考察的时候我觉得可以以这么三道题来考察:
1、A a=new A();a.execute();和IA a=new A();a.execute();执行有什么不同;
2、反射的性能低的原因是?
3、编写一段程序,动态的创建一个接口的实现,并加载到JVM中执行;(可以允许用BCEL等工具)
对象内存的分配和回收
这块涉及的知识点也是比较的多,例如JVM内存区域的划分、自然类型和引用类型的内存分配的不同、TLAB、GC的算法、Sun JDK对于GC的实现、GC触发的时机、GC的跟踪和分析的方法。
在考察的时候我觉得可以以这么三道题来考察:
1、经典的String比较程序题:
String a="a";
String b="b";
String ab="ab";
(a+b)==ab; ?? (引深题,如何才能让(a+b)==ab)
("a"+"b")==ab; ??
2、写一段程序,让其OutOfMemory,或频繁执行Minor GC,但又不触发Full GC,又或频繁执行Full GC,但不执行minor GC,而且不OutOfMemory,甚至可以是控制几次Minor GC后发生一次Full GC;
3、详细讲解GC的实现,例如minor GC的时候导致是怎么回收对象内存的,Full GC的时候是怎么回收对象内存的。
线程和锁机制
这块涉及的知识点仍然是非常的多,例如线程中变量的操作机制、线程调度机制、线程的状态以及控制方法、线程的跟踪和分析方法、同步关键字、lock/unlock的原理等。
在考察的时候我觉得可以以这么几道题考察下:
1、i++的执行过程;
2、一个线程需要等待另外一个线程将某变量置为true才继续执行,如何编写这段程序,或者如何控制多个线程共同启动等;
3、控制线程状态的转换的方法,或者给几个thread dump,分析下哪个线程有问题,问题出在哪;
4、static属性加锁、全局变量属性加锁、方法加锁的不同点?
对于Java程序而言,JVM的关键机制有:字节码的加载、方法的执行、对象内存的分配和回收、线程和锁机制,这几个机制涉及到的jvm的知识点远没有写这几个字这么简单,里面的复杂度还是非常高的。
字节码的加载
JVM通过ClassLoader来完成字节码的动态加载,这里面涉及到的主要是ClassLoader的双亲委派、ClassLoader的编写方法、Class是否被加载的唯一标识以及Class的加载过程。
在考察的时候我觉得可以以这么两道简单的题来考察:
1、写一段将目录中指定的.class文件加载到JVM的程序,并通过Class对象获取到完整类名等信息;
2、一段展示代码,里面包含一个全局静态整型变量,问如果用两个ClassLoader加载此对象,执行这个整型变量++操作后结果会是怎么样的?
方法的执行
JVM有自己的一套指令系统,字节码中即已经是指令了,需要大概掌握了JVM对static、interface、instance、构造器采用的不同的执行方法,另外就是JVM中反射的实现(可以以Sun JDK来举例)、动态代理的实现,最后相关的就是JVM执行字节码的方式(解释、JIT、Hotspot),以及什么时候触发编译成机器码,如何控制。
在考察的时候我觉得可以以这么三道题来考察:
1、A a=new A();a.execute();和IA a=new A();a.execute();执行有什么不同;
2、反射的性能低的原因是?
3、编写一段程序,动态的创建一个接口的实现,并加载到JVM中执行;(可以允许用BCEL等工具)
对象内存的分配和回收
这块涉及的知识点也是比较的多,例如JVM内存区域的划分、自然类型和引用类型的内存分配的不同、TLAB、GC的算法、Sun JDK对于GC的实现、GC触发的时机、GC的跟踪和分析的方法。
在考察的时候我觉得可以以这么三道题来考察:
1、经典的String比较程序题:
String a="a";
String b="b";
String ab="ab";
(a+b)==ab; ?? (引深题,如何才能让(a+b)==ab)
("a"+"b")==ab; ??
2、写一段程序,让其OutOfMemory,或频繁执行Minor GC,但又不触发Full GC,又或频繁执行Full GC,但不执行minor GC,而且不OutOfMemory,甚至可以是控制几次Minor GC后发生一次Full GC;
3、详细讲解GC的实现,例如minor GC的时候导致是怎么回收对象内存的,Full GC的时候是怎么回收对象内存的。
线程和锁机制
这块涉及的知识点仍然是非常的多,例如线程中变量的操作机制、线程调度机制、线程的状态以及控制方法、线程的跟踪和分析方法、同步关键字、lock/unlock的原理等。
在考察的时候我觉得可以以这么几道题考察下:
1、i++的执行过程;
2、一个线程需要等待另外一个线程将某变量置为true才继续执行,如何编写这段程序,或者如何控制多个线程共同启动等;
3、控制线程状态的转换的方法,或者给几个thread dump,分析下哪个线程有问题,问题出在哪;
4、static属性加锁、全局变量属性加锁、方法加锁的不同点?
-------------------------------------------------------------------------------------------
因最近一直在学习 JVM,看到 BlueDavy 的一篇文章 JVM知识点题目,于是便激起了我去解答的兴趣。
字节码的加载
1、写一段将目录中指定的.class文件加载到JVM的程序,并通过Class对象获取到完整类名等信息;
对于 ClassLoader 的加载机制、过程及双亲委派模型等这里就不详细介绍了,基本上属于老生常谈的东西了。不过不了解朋友的可以看一下该作者文章:
深入JVM(4):关于ClassLoader的一些知识
先定义一个 DirectoryClassLoader继承 ClassLoader,通过加载指定的目录下的 class 文件,以下贴出该类的主要代码,完整的代码请从附件中下载。
OK, 接下为写个 Case 测试一把:
输入结果:
Class: Apple - ClassLoader: it.denger.jvm.classloader.DirectoryClassLoader@3d434234
Class: Banana - ClassLoader: it.denger.jvm.classloader.DirectoryClassLoader@3d434234
Class: it.denger.Pear - ClassLoader: it.denger.jvm.classloader.DirectoryClassLoader@3d434234
Class: java.lang.String - ClassLoader: null
目录结构:
可以看出,以上目录下的"所有" class 文件均已经加载,并且所对应的 ClassLoader 都为 DirectoryClassLoader。唯独 java.lang.String 类的 ClassLoader 为 null,其原因是由于 ClassLoader 的双亲委派模型,因为 String 属于 jre 核心库,已由 BootStrap ClassLoader 加载,而 BootStrap 的加载过程是由 C 实现,所以这里自然就是空了。
其实这题还有另外一个更加简单的方法,就是通过 URLClassLoader 进行加载,示例代码如下:
不过我想以上这种通过URLClassLoader的实现这并不是作者所要考察的。
2、一段展示代码,里面包含一个全局静态整型变量,问如果用两个ClassLoader加载此对象,执行这个整型变量++操作后结果会是怎么样的?
继续使用上题的 ClassLoader 对象,修改 Apple.java为:
实际上,当两个ClassLoader加载此Class,并分别通过实例调用 addCount 时,其 count 是2还是3取决于分别加载的这两个 Apple Class 是否相等。
从 JVM加载 Class 的过程来说,对于同一包路径同名的 Class 是不会在同一ClassLoader进行重复加载的,当然,如果如这题所说,使用两个 ClassLoader 实例进行加载同一 Class ,这时候会产生两个 Class实例,原因是由于 ClassLoader 实例之间的相互隔离的。
以下测试用例是使用不同 ClassLoader 实例加载 Apple Class 之后并分别调用 addCount 一次:
~
方法的执行
1、A a=new A();a.execute();和 IA a=new A();a.execute();执行有什么不同;
简单的说,如果想知道方法的执行上有什么不同,那么先看一下它们的bytecode(字节码)有什么不同吧
Java 代码如下:
通过 javap -c it.denger.jvm.code.MethodExecute 命令查看代码的字节码信息,其中 main 方法的字节码如下:
对 exeucte 方法的调用主要在第 9 行和第 21行,不难看出,他们的调用所使用的指令不一样,分别为 invokevirtual(调用对象方法) 和 invokeinterface(调用接口中方法)。
然而以上两个指令在到底在执行时有何不同,这得从 VM 的 Spec 说起了,你可以从以下几个链接中了解个大概:
Java字节码invokeinterface.... invokevirtual的含义和区别
invokeinterface, invokevirtual
What is the point of invokeinterface?
最后当然少不了 VM Spec 了,Sun VM Spec
2、反射的性能低的原因是?
关于这题,在 Sun 官方的 The Reflection API 明确说明了:
大致意思是说因为反射涉及到动态解析类型,以致于某些 jvm 不能够对其进行执行时的优化, 因此使用反射的性能低于非反射的性能..blabla...
上面所说的动态解析,意味着对于对象创建的过程、方法调用的过程是动态,通过动态加载字节码在 JVM 中进行执行,并且当使用 Class.forName 或 getMethod 时会执行权限校验检查以及lookup的过程,以至于这些调用操作必定将存在时间成本。
另外说的JVM不能进行优化,大致是说的是在对 Class 进行编译时候的优化(如在语义分析过程中会自动拆箱装箱处理),因为编译过程中是无法知道反射代码的真正所需要做的事情, 另外也可能无法发挥 JIT 的最大优化潜力。
值一提的是该问题是作者在09年发出,到目前为止,JDK反射提升越来越好了,从1.6版本后基本上与非反射方法调用相差无几了。当然,最好能在反射的代码进行缓存 Class 或 Method 对象,避免重复调用 getMethod 和 Class.forName,从而减少访问检查及lookup 的过程。
3、编写一段程序,动态的创建一个接口的实现,并加载到JVM中执行;(可以允许用BCEL等工具)
既然这里提到可以使用 BCEL 来实现接口的动态实现,那么就直接使用 BCEL 吧,顺便再复习一下 Builder 模式来进行代码的实现,将 Class 的创建与其具体的组成过程进行解耦。
首先定义一个 User 接口:
然后针对该接口动态类生成定义一个 Builder 接口:
对于 User 接口来说,具体实现可能有 Student、Staff等,以下以 Student 为例,定义 StudentClassGenBuilder,动态生成 Student User 及实现 jump 和 run 方法:
可以看出以上已经完成了两个方法的实现,并返回 ClassGen 对象(用于生成具体 Class 的对象)。但该题最终是需要将动态的实现的 Class 加载至 JVM中,并调用动态实现的方法,于是以下的 UserClassGenLoader 便产生了:
至此已经完成从动态接口实现至将其加载至 JVM 中,并获取最终的 Class 对象,最后写一个 TestCase 测试一把:
运行之后显示绿条,并在控制台输出了:
I'm jump....
I'm run....
OK, 至此已经完成本题的所有要求。虽然这里只是简单的实现两个无参、无返回值并且只是 println 输出,但是 BCEL 能做的远远不止这些,理论上来说只要你能手写出的代码基本上都能通过其 API 来动态生成字节码。P.S: 上面代码没写什么注释不过其代码看上去应该比较好懂,有什么可以提出.
~
如果对于以上我个人的分析和理解与你有什么偏差的话,希望能提出一起讨论,另外关于后面两大部分题,现在还在 writing 中,将在近期发出。
字节码的加载
1、写一段将目录中指定的.class文件加载到JVM的程序,并通过Class对象获取到完整类名等信息;
对于 ClassLoader 的加载机制、过程及双亲委派模型等这里就不详细介绍了,基本上属于老生常谈的东西了。不过不了解朋友的可以看一下该作者文章:
深入JVM(4):关于ClassLoader的一些知识
先定义一个 DirectoryClassLoader继承 ClassLoader,通过加载指定的目录下的 class 文件,以下贴出该类的主要代码,完整的代码请从附件中下载。
- package it.denger.jvm.classloader;
- package it.denger.jvm.classloader;
- public class DirectoryClassLoader extends ClassLoader {
- protected File directory;
- protected ClassLoader parent;
- public DirectoryClassLoader(File directory, ClassLoader parent) {
- // 将父 ClassLoader 传入super,用于classloader加载时向上查找
- super(parent);
- .....
- }
- public Class<?>[] findAndLoadAllClassInDirectory(){
- // 获取当前目录中所有 class 文件的相对路径
- String[] classPaths = getAllClassRelativePathInDirectory(directory);
- List<Class<?>> classes = new ArrayList<Class<?>>(classPaths.length);
- for (String classPath : classPaths){
- try {
- // 将所有 class 文件相对路径转换为相应的包名
- // 如 it/denger/Pear.class 则转换为 it.denger.Pear
- String className = convertPathToClassName(classPath);
- // 调用父类的 loadClass,该方法实现通过向上 一级级的ClassLoader
- // 进行查找,当所有上级全部无法找到时则会调用本ClassLoader的
- // findClass 方法进行查找,也就是所谓的 “双亲委派模型”
- Class<?> classz = loadClass(className);
- if (classes != null){
- classes.add(classz);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- return classes.toArray(new Class<?>[0]);
- }
- // 重写父类 findClass,当所有 Parent Class Loader 无法找到类时,
- // 则会通过调用这里所该方法进行最后的查找
- protected java.lang.Class<?> findClass(String name) throws ClassNotFoundException {
- Class<?> classz = null;
- // 通过 className 获取到对应的 File 对象
- File classFile = new File(directory, convertClassNameToPath(name));
- if (classFile.exists()) {
- try {
- // 重点在这里,从 class 的文件流中加载 Class 对象
- classz = loadClassFromStream(name, new FileInputStream(classFile), (int)classFile.length());
- } catch (Exception e) {
- throw new ClassNotFoundException(String.format("Find class %s error!", name), e);
- }
- }
- .....
- return classz;
- }
- protected byte[] loadBytesFromStream(InputStream stream, int len) throws IOException{
- byte[] streamBytes = new byte[len];
- int read, off = 0;
- while(len > 0 && (read = stream.read(streamBytes, off, len)) != -1 ){
- off += read;
- len -= read;
- }
- return streamBytes;
- }
- // 通过调用父类的 defineClass 将 字节 转为 Class 对象
- protected Class<?> loadClassFromBytes(String name, byte []classBytes){
- return defineClass(name, classBytes, 0, classBytes.length);
- }
- protected Class<?> loadClassFromStream(String name, InputStream stream, int len){
- return loadClassFromBytes(name, loadBytesFromStream(stream, len));
- }
- }
OK, 接下为写个 Case 测试一把:
- public class DirectoryClassLoaderTest extends TestCase{
- protected DirectoryClassLoader directoryClassLoader;
- protected String classDirectory;
- protected void setUp() throws Exception {
- classDirectory = "/Users/denger/Workspaces/Java/backup/classes";
- directoryClassLoader = new DirectoryClassLoader(new File(classDirectory), this.getClass().getClassLoader());
- }
- public void testShouldBeLoadedAllClassFileInDirectory(){
- Class<?>[] classes = directoryClassLoader.findAndLoadAllClassInDirectory();
- assertTrue(classes.length > 0);
- for(Class<?> classz : classes){
- assertNotNull(classz);
- System.out.println("Class: " + classz.getName() + " - ClassLoader: " + classz.getClassLoader());
- }
- }
Class: Apple - ClassLoader: it.denger.jvm.classloader.DirectoryClassLoader@3d434234
Class: Banana - ClassLoader: it.denger.jvm.classloader.DirectoryClassLoader@3d434234
Class: it.denger.Pear - ClassLoader: it.denger.jvm.classloader.DirectoryClassLoader@3d434234
Class: java.lang.String - ClassLoader: null
目录结构:
可以看出,以上目录下的"所有" class 文件均已经加载,并且所对应的 ClassLoader 都为 DirectoryClassLoader。唯独 java.lang.String 类的 ClassLoader 为 null,其原因是由于 ClassLoader 的双亲委派模型,因为 String 属于 jre 核心库,已由 BootStrap ClassLoader 加载,而 BootStrap 的加载过程是由 C 实现,所以这里自然就是空了。
其实这题还有另外一个更加简单的方法,就是通过 URLClassLoader 进行加载,示例代码如下:
- String classFileDirectory = "file:///Users/denger/Workspaces/Java/backup/classes/";
- URLClassLoader classLoader = new URLClassLoader(new URL[]{new URL(classFileDirectory)});
- System.out.println(classLoader.loadClass("it.denger.Pear"));
2、一段展示代码,里面包含一个全局静态整型变量,问如果用两个ClassLoader加载此对象,执行这个整型变量++操作后结果会是怎么样的?
继续使用上题的 ClassLoader 对象,修改 Apple.java为:
- public class Apple{
- public static int count = 1;
- public static void addCount(){
- count++;
- }
- public static int getCount(){
- return count;
- }
- }
从 JVM加载 Class 的过程来说,对于同一包路径同名的 Class 是不会在同一ClassLoader进行重复加载的,当然,如果如这题所说,使用两个 ClassLoader 实例进行加载同一 Class ,这时候会产生两个 Class实例,原因是由于 ClassLoader 实例之间的相互隔离的。
以下测试用例是使用不同 ClassLoader 实例加载 Apple Class 之后并分别调用 addCount 一次:
- public void testRepeatedLoadClass() {
- DirectoryClassLoader steveDirectoryClassLoader = new DirectoryClassLoader(
- new File(classDirectory), this.getClass().getClassLoader());
- DirectoryClassLoader myDirectoryClassLoader = new DirectoryClassLoader(
- new File(classDirectory), this.getClass().getClassLoader());
- Class<?> steveApple = steveDirectoryClassLoader.loadClass("Apple");
- Class<?> myApple = myDirectoryClassLoader.loadClass("Apple");
- // 产生不同的 Apple Class 实例,原因是以上两个 ClassLoader 实例是相互隔离的,他们都并知道对方加载了哪些 Class
- assertTrue(steveApple != myApple);
- // 分别调用 addCount
- steveApple.getMethod("addCount").invoke(null);
- myApple.getMethod("addCount").invoke(null);
- // 其 count 都为2,都只是 ++ 了自己的 count
- assertTrue(Integer.parseInt(steveApple.getMethod("getCount").invoke(null).toString()) == 2);
- assertTrue(Integer.parseInt(steveApple.getMethod("getCount").invoke(null).toString()) == Integer.parseInt(myApple.getMethod("getCount").invoke(null).toString()));
- }
~
方法的执行
1、A a=new A();a.execute();和 IA a=new A();a.execute();执行有什么不同;
简单的说,如果想知道方法的执行上有什么不同,那么先看一下它们的bytecode(字节码)有什么不同吧
Java 代码如下:
- package it.denger.jvm.code;
- public class MethodExecute {
- public static void main(String[] args) {
- A a = new A();
- a.execute();
- IA ia = new A();
- ia.execute();
- }
- }
- interface IA{
- public void execute();
- }
- class A implements IA{
- public void execute() {
- System.out.println("A execute call....");
- }
- }
- public static void main(java.lang.String[]);
- Code:
- 0: new #2; //class it/denger/jvm/code/A
- 3: dup
- 4: invokespecial #3; //Method it/denger/jvm/code/A."<init>":()V
- 7: astore_1
- 8: aload_1
- 9: invokevirtual #4; //Method it/denger/jvm/code/A.execute:()V
- 12: new #2; //class it/denger/jvm/code/A
- 15: dup
- 16: invokespecial #3; //Method it/denger/jvm/code/A."<init>":()V
- 19: astore_2
- 20: aload_2
- 21: invokeinterface #5, 1; //InterfaceMethod it/denger/jvm/code/IA.execute:()V
- 26: return
- }
然而以上两个指令在到底在执行时有何不同,这得从 VM 的 Spec 说起了,你可以从以下几个链接中了解个大概:
Java字节码invokeinterface.... invokevirtual的含义和区别
invokeinterface, invokevirtual
What is the point of invokeinterface?
最后当然少不了 VM Spec 了,Sun VM Spec
2、反射的性能低的原因是?
关于这题,在 Sun 官方的 The Reflection API 明确说明了:
引用
Because reflection involves types that are dynamically resolved, certain Java virtual machine optimizations can not be performed. Consequently, reflective operations have slower performance than their non-reflective counterparts, and should be avoided in sections of code which are called frequently in performance-sensitive applications.
大致意思是说因为反射涉及到动态解析类型,以致于某些 jvm 不能够对其进行执行时的优化, 因此使用反射的性能低于非反射的性能..blabla...
上面所说的动态解析,意味着对于对象创建的过程、方法调用的过程是动态,通过动态加载字节码在 JVM 中进行执行,并且当使用 Class.forName 或 getMethod 时会执行权限校验检查以及lookup的过程,以至于这些调用操作必定将存在时间成本。
另外说的JVM不能进行优化,大致是说的是在对 Class 进行编译时候的优化(如在语义分析过程中会自动拆箱装箱处理),因为编译过程中是无法知道反射代码的真正所需要做的事情, 另外也可能无法发挥 JIT 的最大优化潜力。
值一提的是该问题是作者在09年发出,到目前为止,JDK反射提升越来越好了,从1.6版本后基本上与非反射方法调用相差无几了。当然,最好能在反射的代码进行缓存 Class 或 Method 对象,避免重复调用 getMethod 和 Class.forName,从而减少访问检查及lookup 的过程。
3、编写一段程序,动态的创建一个接口的实现,并加载到JVM中执行;(可以允许用BCEL等工具)
既然这里提到可以使用 BCEL 来实现接口的动态实现,那么就直接使用 BCEL 吧,顺便再复习一下 Builder 模式来进行代码的实现,将 Class 的创建与其具体的组成过程进行解耦。
首先定义一个 User 接口:
然后针对该接口动态类生成定义一个 Builder 接口:
- package it.denger.jvm.code.bcel;
- import org.apache.bcel.generic.ClassGen;
- public interface UserClassGenBuilder {
- static final String USER_CLASS_NAME = "it.denger.jvm.code.bcel.IUser";
- UserClassGenBuilder init(String className);
- UserClassGenBuilder implementJumpMethod();
- UserClassGenBuilder implementRunMethod();
- ClassGen build();
- }
- public class StudentClassGenBuilder implements UserClassGenBuilder{
- protected String className;
- protected ClassGen classGen;
- public StudentClassGenBuilder init(String className) {
- this.className = className;
- classGen = new ClassGen(className, "java.lang.Object",
- "<generated>", ACC_PUBLIC | ACC_SUPER,
- new String[] { USER_CLASS_NAME });
- classGen.addEmptyConstructor(ACC_PUBLIC);
- return this;
- }
- public UserClassGenBuilder implementJumpMethod() {
- InstructionFactory instructionFactory = new InstructionFactory(classGen);
- InstructionList instructionList = new InstructionList();
- ConstantPoolGen constantPool = classGen.getConstantPool();
- MethodGen methodGen = new MethodGen(ACC_PUBLIC,
- Type.VOID, new Type[0], new String[0],
- "jump", className, instructionList, constantPool);
- instructionList.append(instructionFactory.createPrintln("I'm jump...."));
- instructionList.append(InstructionFactory.createReturn(Type.VOID));
- methodGen.setMaxStack();
- classGen.addMethod(methodGen.getMethod());
- return this;
- }
- public UserClassGenBuilder implementRunMethod() {
- InstructionFactory instructionFactory = new InstructionFactory(classGen);
- InstructionList instructionList = new InstructionList();
- ConstantPoolGen constantPool = classGen.getConstantPool();
- MethodGen methodGen = new MethodGen(ACC_PUBLIC,
- Type.VOID, new Type[0], new String[0],
- "run", className, instructionList, constantPool);
- instructionList.append(instructionFactory.createPrintln("I'm run...."));
- instructionList.append(InstructionFactory.createReturn(Type.VOID));
- methodGen.setMaxStack();
- classGen.addMethod(methodGen.getMethod());
- return this;
- }
- public ClassGen build() {
- return classGen;
- }
- public class UserClassGenLoader {
- protected UserClassGenBuilder builder;
- public UserClassGenLoader(UserClassGenBuilder builder){
- this.builder = builder;
- }
- public Class<?> loadClass(String className) throws ClassNotFoundException, IOException{
- ClassGen classGen = this.builder.init(className)
- .implementJumpMethod().implementRunMethod().build();
- return loadClassForClassGen(classGen);
- }
- protected Class<?> loadClassForClassGen(ClassGen classGen) throws ClassNotFoundException, IOException{
- ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
- classGen.getJavaClass().dump(arrayOutputStream);
- byte[] classBytes = arrayOutputStream.toByteArray();
- Map<String, byte[]> classByteMap = new HashMap<String, byte[]>();
- classByteMap.put(classGen.getClassName(), classBytes);
- ByteClassLoader byteClassLoader = new ByteClassLoader(this.getClass()
- .getClassLoader(), classByteMap);
- return byteClassLoader.loadClass(classGen.getClassName());
- }
- }
- public void testShouldBeLoadStudentNewInstance() {
- try {
- Class<?> studentClass = new UserClassGenLoader(new StudentClassGenBuilder())
- .loadClass("it.denger.jvm.code.bcel.Student");
- IUser studentUser = (IUser) studentClass.newInstance();
- assertNotNull(studentUser);
- assertEquals(studentUser.getClass().getName(), "it.denger.jvm.code.bcel.Student");
- studentUser.jump();
- studentUser.run();
- } catch (Exception e) {
- fail(e.getMessage());
- }
- }
- }
运行之后显示绿条,并在控制台输出了:
I'm jump....
I'm run....
OK, 至此已经完成本题的所有要求。虽然这里只是简单的实现两个无参、无返回值并且只是 println 输出,但是 BCEL 能做的远远不止这些,理论上来说只要你能手写出的代码基本上都能通过其 API 来动态生成字节码。P.S: 上面代码没写什么注释不过其代码看上去应该比较好懂,有什么可以提出.
~
如果对于以上我个人的分析和理解与你有什么偏差的话,希望能提出一起讨论,另外关于后面两大部分题,现在还在 writing 中,将在近期发出。
- dev-source.tar (38.5 KB)
- 下载次数: 0
相关推荐
Java虚拟机(JVM)是Java程序运行的核心,它负责解释和执行字节码,为Java应用程序提供了一个跨平台的运行环境。JDK(Java Development Kit)包含了开发和运行Java程序所需的所有工具,包括JVM。当我们谈论"jdk,jvm...
JVM 内存溢出问题解析 JVM 内存溢出是指程序运行所需的内存大于虚拟机能提供的最大内存的情况。这种情况可能是由于数据量过大、死循环、静态变量和静态方法过多、递归、无法确定是否被引用的对象等原因引起的。同时...
### JVM问题诊断常用命令详解 在Java开发与运维过程中,针对JVM(Java虚拟机)进行性能调优、故障排查是非常重要的环节。本文将详细介绍三种常用的JVM问题诊断工具:`jinfo`、`jmap` 和 `jstack` 的功能、用法以及...
jvm优化及问题定位 架构师培训 讲解-----------------------------------------------------------------------------------
以上只是JVM面试中的一小部分问题,深入理解和掌握JVM的工作原理,能够帮助开发者解决性能问题,提升应用的稳定性。对于Java开发人员来说,不断学习和实践JVM相关知识,是提升自身技能的重要途径。
通过掌握JVM的工作原理和技术细节,开发者不仅能够编写出更加高效、安全的代码,还能更好地解决程序运行过程中出现的问题。随着技术的发展,JVM也在不断地演进和完善,以适应新的需求和挑战。未来,我们可以期待看到...
标题中提到了JVM原理、JVM调优、JVM内存模型和JAVA并发,这些都是Java虚拟机(JVM)相关的核心概念。JVM是运行Java字节码的虚拟计算机,为Java提供了一个跨平台的环境,确保Java程序可以在不同的操作系统上运行而...
JVM 输出 GC 日志导致 JVM 卡住是一个常见的问题,尤其是在高并发和高性能应用中。这个问题的根源在于 JVM 的垃圾回收机制(Garbage Collection,GC),它会在 JVM 运行时周期性地进行垃圾回收,以释放内存空间。...
Java虚拟机(JVM)是Java程序运行的基础,它是一个抽象的计算机系统,负责执行Java字节码。本文将深入探讨JVM的启动过程...通过深入研究JVM,开发者可以更好地解决内存溢出、性能瓶颈等问题,实现更高效的Java应用。
3. **线程状态**:`jvm-mon`可展示当前运行的线程数量及状态,如RUNNABLE、WAITING、TIMED_WAITING等,这对于排查线程阻塞和死锁问题非常有帮助。 4. **类加载统计**:显示已加载的类数量,帮助分析类加载是否正常...
### JVM 详细介绍:掌握 JVM 的各个组成部分与功能 #### 一、Java 源文件编译及执行 Java 应用程序的核心...理解 JVM 的内部工作原理有助于开发者更好地编写高质量的 Java 应用程序,并有效解决运行时遇到的问题。
jmap、jstack、jstat组合使用定位jvm问题
3. **内存模型**:包括堆内存、栈内存、方法区(在Java 8之后变为元空间)、程序计数器、本地方法栈等,理解它们的作用有助于避免内存溢出和内存泄漏问题。 4. **垃圾收集**:JVM如何自动管理内存,理解不同垃圾...
对于开发者来说,了解JVM的内部实现有助于优化代码和解决问题。例如,理解JIT(Just-In-Time)编译器的工作原理,可以知道何时Java代码会被编译为本地机器码,从而提升运行效率。 ### 总结 Java虚拟机作为Java技术...
解决 JVM 中的 PermGen space 问题 PermGen space(Permanent Generation space)是 JVM 中的一块永久保存区域,用于存放 Class 和 Meta 信息。当应用程序加载 Class 时,Class 就会被放入 PermGen space 区域中,...
Java虚拟机(JVM)是Java程序...了解并掌握这些JVM常见问题,不仅有助于解决实际开发中的性能问题,还能在面试中展现出深厚的Java基础。对于Java开发者来说,深入理解JVM的工作原理和调优策略是不可或缺的专业技能。
在这个压缩包中,"JVM图解.png"可能是对JVM内部结构的可视化表示,"JVM图解"可能是一个详细的文档,解释了JVM的工作原理,而"JVM指令手册 中文版"则提供了JVM可执行的所有指令的详细信息。下面,我们将深入探讨JVM的...
- `buildinfo.txt`:提供了关于构建过程的信息,如编译日期、版本号等,有助于诊断问题或确定软件的构建状态。 - `src.zip`:包含了源代码,允许开发者查看和分析SAP JVM的实现细节,便于调试和学习。 - `bin`:...
解决这些问题的关键在于深入理解JVM。 #### JVM的本质与架构 JVM是一种在物理计算机上运行的抽象计算机,不同于可见的虚拟化软件如VMWare,它主要存在于内存中。其设计目标是实现“一次编译,处处运行”,通过将...
在Java开发中,JVM(Java虚拟机)调优是一项关键技能,它涉及到性能优化、资源管理以及问题排查。本文将探讨两种常见的JVM调优场景:CPU占用过高和死锁。 首先,我们来看CPU占用过高的情况。当CPU使用率长时间居高...