第六个周末-坚持住


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)类加载器:是用本地代码实现的类装入器,它负责将 /lib下面的类库加载到内存中(比如rt.jar)。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。

标准扩展(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的错误了。



上一篇  servlet总结(一)到底servlet是什么? 下一篇   第五个周末-沉下心来