2016-06-12 到 2016-06-19
> 你活着是为了什么?
字节码
localhost:sthrift zhailz$ javac -g:vars ATestValue.java
localhost:sthrift zhailz$ javap -verbose ATestValue
能够看到文件ATestValue的字节码,这个源文件是:
public class ATestValue {
public static void main(String[] args) {
HashMap<String, String> value = new HashMap<>(7);
value.put("a", "b");
value.put("a5", "b");
System.out.println(value);
}
}
生成的字节码是:
Classfile /Users/zhailz/Documents/workspace/idle-workspace/Sthrift/src/test/java/sthrift/ATestValue.class
Last modified 2016-7-4; size 754 bytes
MD5 checksum d8d71a61f658e04fc5951972e3d07ee3
public class sthrift.ATestValue
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #11.#26 // java/lang/Object."<init>":()V
#2 = Class #27 // java/util/HashMap
#3 = Methodref #2.#28 // java/util/HashMap."<init>":(I)V
#4 = String #29 // a
#5 = String #30 // b
#6 = Methodref #2.#31 // java/util/HashMap.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
#7 = String #32 // a5
#8 = Fieldref #33.#34 // java/lang/System.out:Ljava/io/PrintStream;
#9 = Methodref #35.#36 // java/io/PrintStream.println:(Ljava/lang/Object;)V
#10 = Class #37 // sthrift/ATestValue
#11 = Class #38 // java/lang/Object
#12 = Utf8 <init>
#13 = Utf8 ()V
#14 = Utf8 Code
#15 = Utf8 LocalVariableTable
#16 = Utf8 this
#17 = Utf8 Lsthrift/ATestValue;
#18 = Utf8 main
#19 = Utf8 ([Ljava/lang/String;)V
#20 = Utf8 args
#21 = Utf8 [Ljava/lang/String;
#22 = Utf8 value
#23 = Utf8 Ljava/util/HashMap;
#24 = Utf8 LocalVariableTypeTable
#25 = Utf8 Ljava/util/HashMap<Ljava/lang/String;Ljava/lang/String;>;
#26 = NameAndType #12:#13 // "<init>":()V
#27 = Utf8 java/util/HashMap
#28 = NameAndType #12:#39 // "<init>":(I)V
#29 = Utf8 a
#30 = Utf8 b
#31 = NameAndType #40:#41 // put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
#32 = Utf8 a5
#33 = Class #42 // java/lang/System
#34 = NameAndType #43:#44 // out:Ljava/io/PrintStream;
#35 = Class #45 // java/io/PrintStream
#36 = NameAndType #46:#47 // println:(Ljava/lang/Object;)V
#37 = Utf8 sthrift/ATestValue
#38 = Utf8 java/lang/Object
#39 = Utf8 (I)V
#40 = Utf8 put
#41 = Utf8 (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
#42 = Utf8 java/lang/System
#43 = Utf8 out
#44 = Utf8 Ljava/io/PrintStream;
#45 = Utf8 java/io/PrintStream
#46 = Utf8 println
#47 = Utf8 (Ljava/lang/Object;)V
{
public sthrift.ATestValue();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lsthrift/ATestValue;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=2, args_size=1
0: new #2 // class java/util/HashMap
3: dup
4: bipush 7
6: invokespecial #3 // Method java/util/HashMap."<init>":(I)V
9: astore_1
10: aload_1
11: ldc #4 // String a
13: ldc #5 // String b
15: invokevirtual #6 // Method java/util/HashMap.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
18: pop
19: aload_1
20: ldc #7 // String a5
22: ldc #5 // String b
24: invokevirtual #6 // Method java/util/HashMap.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
27: pop
28: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
31: aload_1
32: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
35: return
LocalVariableTable:
Start Length Slot Name Signature
0 36 0 args [Ljava/lang/String;
10 26 1 value Ljava/util/HashMap;
LocalVariableTypeTable:
Start Length Slot Name Signature
10 26 1 value Ljava/util/HashMap<Ljava/lang/String;Ljava/lang/String;>;
}
在这个字节码里面,你可以看到常量池(Constant pool),保存对应的常量。栈帧(LocalVariableTable),堆区还有计数器等等的内容,也可以更清晰的明白字节码增强技术的原理。
字节码加载
有一个比较响亮的,一般都会知道的机制:双亲委派机制。具体的源码是(rt.jar包中的java.lang.ClassLoader类中):
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{
// First, check if the class has already been loaded
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
JVM预定义的三种类型类加载器:
启动(Bootstrap)类加载器:是用本地代码实现的类装入器,它负责将
标准扩展(Extension)类加载器:是由 Sun 的 ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现的。它负责将< Java_Runtime_Home >/lib/ext或者由系统变量 java.ext.dir指定位置中的类库加载到内存中。开发者可以直接使用标准扩展类加载器。
系统(System)类加载器:是由 Sun 的 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。它负责将系统类路径(CLASSPATH)中指定的类库加载到内存中。开发者可以直接使用系统类加载器。 除了以上列举的三种类加载器,还有一种比较特殊的类型 — 线程上下文类加载器。
双亲委派机制描述 某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。
两个思考:
1.如果我们定义了一个:java.lang.String,会出现什么情况?能够加载到吗?怎么才能够加载到呢?
测试代码:
public static void main(String[] args){
String value = new String();
value.repeate("ppp");
}
错误代码:
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.SecurityException: Prohibited package name: java.lang
2.类加载的过程是单线程的吗?如果使用了没有加载的类会出现什么情况?
确实是单线程的模式,所以我们的单例模式可以使用静态内部类来实现。但是不能够在静态的类中加入过多的逻辑,因为如果加载过程过长的话,多线程使用这个类的时候,就可能出现:NoDefClassFind的错误了。