虚拟机是有规范的,HotSpot是sun公司也就是现在的oracle公司提供的,虚拟机并不是只有oracle有,IBM也有虚拟机,虚拟机是有规范的, 只要遵循这个规范就可以自己造虚拟机。虚拟机也有商用的,收费很高。Oracle是免费的虚拟机。Openjdk是基于jdk的开源实现,open是开源的意思。
HotSpot是oracle的,是使用的很广泛的。
Java代码中,类型的加载,链接,初始化都是在程序运行期间runtime完成的(其他的一些语言是编译期间完成的)。类型是定义的一个类、接口、枚举,不是对象。
动态代理是类型在运行期间动态的生成出来,在编译期间是没有的。
Java是静态语言,但是也有很多动态的特性。
加载:class文件从磁盘到内存。
连接:类与类的关系确定好,字节码的验证校验。字节码有问题虚拟机就不会去执行。字节码文件是可以人为操纵的。
初始化:类的静态变量赋值。都是在运行期间完成的,不是编译期间。
类加载器:每一个类型(类)会被纳入jvm管辖范围中。加载类的工具。
虚拟机就是一个进程,进程在某些情况下就会停掉:
- 程序执行了System.exit()
- 程序正常结束
- 执行过程中遇到了异常
- 操作系统层面出现错误导致虚拟机进程结束
符号引用就是字符串,写的时候,直接引用就是去找相应的地址。
类的卸载:类的信息从内存移除。
加载:类加载到内存,.class文件的二进制数据读入到内存,放入到方法区中,然后在内存创建一个Class对象,虚拟机规范并没有规定Class对象放在哪里,Oracle的HotSpot虚拟机将其放在了方法区中(没有放到堆区),Class对象封装类在方法区的数据结构。所有的实例都会对应一份Class对象。所以反射要使用Class对象。
验证:验证类的正确性
准备:静态变量赋予初始值,分配相应的空间大小。
解析:符号引用转换为直接引用
初始化:赋予正确的初始值
加载链接初始化:加载到内存、校验,赋予默认值、正确值,符号引用转换。
加载class文件的方式:
- 磁盘直接加载,位于classpath类路径下
- 网络下载.class文件
- Zip,jar文件加载.class文件
- 数据库提取.class文件(使用的少)
- Java源文件动态编译为.class文件(动态代理)。运行期生成,编译时没有。jsp文件转换成class文件。
Java对类的使用方式:
- 主动使用
- 被动使用
类或接口必须首次主动使用才初始化(初始化就会执行里面的static代码块)。不首次主动使用也会加载,只是不初始化。
JVM规范允许加载器预料某个类将要使用时候预先加载它,如果预先加载中遇到了class文件错误,类加载器要在首次主动使用这个类时候报告错误。入股这个类一直没有首次使用,就一直不报错。
主动使用7种:
- 创建实例
- 访问(读写)类的静态变量。调用类的静态方法
- 反射
- 初始化类的子类(初始化之类时候父类也是主动使用,也会初始化,爷爷类也会初始化)
- 被标记为启动类的类(main函数了类,入口类)
其他使用类的方式都是被动使用,都不会导致类的初始化。有可能导致类的加载和链接。
调用ClassLoader类的loadClass方法加载一个类,并不是对类的主动使用,不会导致类的初始化。
先让父类加载器加载,加载不了在自己加载,是为了安全。
表面看是一种继承关系,实际上是一种包含关系。
public class 额鹅鹅鹅 { public static void main(String[] args) { System.out.println(Child.p); } } class Parent{ static String p = "parent"; static { System.out.println("parent static"); } } class Child extends Parent{ static String c = "Child"; static { System.out.println("Child static"); } } /* System.out.println(Child.c) parent static Child static Child 使用子类的静态属性,是对子类的主动使用,父类的静态代码块执行,子类的静态代码块会执行。 System.out.println(Child.p) parent static parent 使用父类的静态属性,不是对子类的主动使用,父类的静态代码块执行,子类的静态代码块不会执行。 */
-XX:+TraceClassLoading,追踪类的加载信息
-XX:+TraceClassUnloading,追踪类的卸载信息
-XX:+<option>:开启option选项
-XX:-<option>:关闭option选项
-XX:<option>=value:设置option选项值
Jvm的参数有上千个,
如何在windows平台下使用hsdis与jitwatch查看JIT后的汇编码
package com.ssss; class A { static final String c = "Child";//final是常量,编译阶段,常量放在调用这个常量的方法所在的类的常量池中。就是TT类的常量池中。 //TT类并没有直接引用A类,因此使用A.c这个常量的时候,不会触发A类的初始化,因此Child static不会打印。 //因为是在TT类的常量池中,因此TT类和A类没有关系了。甚至将A类的class文件删除都可以。(不能删源文件,否则就不能编译了) volatile int s = 1; static { System.out.println("Child static"); } public synchronized int j() { return s++; } }/* Child */ // javap 查看java编译器为我们生成的字节码汇编代码。 /* C:\Users\Admin\Desktop\图片\111>javap -c A.class class ssss.A { static final java.lang.String c;//属性 static {};//静态 Code: 0: getstatic #13 // Field java/lang/System.out:Ljava/io/PrintStream :System.out.println 3: ldc #19 // String Child static 5: invokevirtual #21 // Method java/io/PrintStream.println:(Ljava/lang/String;)V :System.out.println 8: return ssss.A();//方法 Code: 0: aload_0 1: invokespecial #30 // Method java/lang/Object."<init>":()V 4: return } C:\Users\Admin\Desktop\图片\111>javap -c TT.class Compiled from "TT.java" public class ssss.TT { public ssss.TT(); Code: 0: aload_0 1: invokespecial #8 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream :System.out.println 3: ldc #22 // String Child ,A.c已经是Child了,编译时候就已经是Child了。ldc将int,float,String推到栈顶。 5: invokevirtual #24 // Method java/io/PrintStream.println:(Ljava/lang/String;)V :System.out.println 8: return } */ public class TT { public static void main(String[] args) { System.out.println(A.c); } }
class abc { //常量final UUID.randomUUID()不是编译期间可以确定值,那么编译时候就不会把str的值放入 //DDD类的常量池中,运行时候,就会主动使用abc类,导致abc类的初始化。否则abc类是不会初始化的(abc的static代码块就不会执行)。 public static final String str = UUID.randomUUID().toString(); static { System.out.println("static"); } } public class DDD{ public static void main(String[] args) { System.out.println(abc.str); } }
class abc { static { System.out.println("static abc"); } } public class DDD{ public static void main(String[] args) { abc a = new abc();//静态代码块执行 System.out.println(a.getClass());//class com.ssss.abc System.out.println(a.getClass().getSuperclass());//class java.lang.Object abc[] ss = new abc[1];//不会执行静态代码块,数组不是主动使用, System.out.println(ss.getClass());//class [Lcom.ssss.abc; System.out.println(ss.getClass().getSuperclass());//class java.lang.Object int[] i = new int[1];//,不是主动使用, System.out.println(i.getClass());//class [I I是int类型 System.out.println(i.getClass().getSuperclass());//class java.lang.Object char[] c = new char[1];//,不是主动使用, System.out.println(c.getClass());//class [C C是char类型 System.out.println(c.getClass().getSuperclass());//class java.lang.Object boolean[] b = new boolean[1];//,不是主动使用, System.out.println(b.getClass());//class [Z Z是boolean类型 System.out.println(b.getClass().getSuperclass());//class java.lang.Object short[] s = new short[1];//,不是主动使用, System.out.println(s.getClass());//class [S S是short类型 System.out.println(s.getClass().getSuperclass());//class java.lang.Object long[] l = new long[1];//,不是主动使用, System.out.println(l.getClass());//class [J J是long类型 System.out.println(l.getClass().getSuperclass());//class java.lang.Object byte[] by = new byte[1];//,不是主动使用, System.out.println(by.getClass());//class [B B是byte类型 System.out.println(by.getClass().getSuperclass());//class java.lang.Object } }
//一个接口初始化,并不要求父接口也初始化。 //真正使用父接口时候(如使用接口中的常量时候)才不会初始化。类的初始化是会初始化父类的。 //final常量是会放到调用类的常量池中去的,不会引起定义常量的类的初始化,运行期间就放进去了。 //如果常量是随机数运行期间才能确定的,那么就会引起定义常量的类的初始化。 public class sd { public static void main(String[] args) { System.out.println(Ic.b); } } interface Ip { public static int a = 5; } interface Ic extends Ip { public static int b = 6; }
package com.ssss; public class sd { @SuppressWarnings("static-access") public static void main(String[] args) { Singleton ss = Singleton.get();//先去初始化Singleton类,最后调用get方法。 System.out.println(ss.i);//1 System.out.println(ss.j);//0 } } @SuppressWarnings("static-access") class Singleton { public static int i; /*static { System.out.println("s "+s );//s报错,找不到定义。 }*/ public static Singleton s = new Singleton();//看到new就会去执行构造函数。在执行下面的静态块。否则不执行构造函数。 static { System.out.println("s.i2:"+s.i);//1 System.out.println("s.j2:"+s.j);//1 } private Singleton() { System.out.println("i0:"+i);//i:0 System.out.println("j0:"+j);//j:0, i++; j++; System.out.println("i:"+i);//i:1 System.out.println("j:"+j);//j:1,j这里是1,但是后面把j赋值为了0,所以get时候是0. System.out.println("s.i:"+s.i);//1 System.out.println("s.j:"+s.j);//1 } // public static int j = 0;//就是执行这里,再去执行get方法,把对象的属性j赋值成了0 public static Singleton get() { System.out.println("i1:"+i);//i1:1 System.out.println("j1:"+j);//j1:0 System.out.println("s.i1:"+s.i);//1 System.out.println("s.j1:"+s.j);//0 return s; } static { System.out.println("s.i3:"+s.i);//1 System.out.println("s.j3:"+s.j);//1 } public static int j = 0;//就是执行这里,再去执行get方法,把对象的属性j赋值成了0。即使这行代码放在这里,j也会是0 //因为先初始化Singleton类,get方法最后调用。即使get方法是静态的,也是在main函数中调用的时候再去执行,不会初始化执行,初始化只会执行静态代码块。构造函数也只是有new才会执行。初始化从上到下执行。 static { System.out.println("s.i4:"+s.i);//1 System.out.println("s.j4:"+s.j);//0 } } /*i0:0 j0:0 i:1 j:1 s.i:1 s.j:1 s.i2:1 s.j2:1 s.i3:1 s.j3:1 s.i4:1 s.j4:0 i1:1 j1:0 s.i1:1 s.j1:0 1 0*/
package com.ssss; public class sd { @SuppressWarnings("static-access") public static void main(String[] args) { Singleton ss = Singleton.get();//先去初始化Singleton类,最后调用get方法。 System.out.println(ss.i);//1 System.out.println(ss.j);//0 } } @SuppressWarnings("static-access") class Singleton { public static int i; public static Singleton s = new Singleton();//看到new就会去执行构造函数。在执行下面的静态块。否则不执行构造函数。 public static int j = 0; static {//不加static,则是最先执行,然后执行构造函数,且每个对象都会执行一遍 System.out.println("s.i2:"+s.i);//1 System.out.println("s.j2:"+s.j);//0 } private Singleton() { System.out.println("i0:"+i);//i:0 System.out.println("j0:"+j);//j:0, i++; j++; System.out.println("i:"+i);//i:1 System.out.println("j:"+j);//j:1, System.out.println("s.i:"+s.i);//1 System.out.println("s.j:"+s.j);//1 } public static Singleton get() { System.out.println("i1:"+i);//i1:1 System.out.println("j1:"+j);//j1:0 System.out.println("s.i1:"+s.i);//1 System.out.println("s.j1:"+s.j);//0 return s; } static { System.out.println("s.i3:"+s.i);//1 System.out.println("s.j3:"+s.j);//0 } } /*i0:0 j0:0 i:1 j:1 s.i:1 s.j:1 s.i2:1 s.j2:0 s.i3:1 s.j3:0 i1:1 j1:0 s.i1:1 s.j1:0 1 0*/
package com.ssss; public class sd { @SuppressWarnings("static-access") public static void main(String[] args) { Singleton ss = Singleton.get();//先去初始化Singleton类,最后调用get方法。 System.out.println(ss.i);//1 System.out.println(ss.j);//1 } } @SuppressWarnings("static-access") class Singleton { public static int i; public static int j = 0; public static Singleton s = new Singleton();//看到new就会去执行构造函数。在执行下面的静态块。否则不执行构造函数。 static { System.out.println("s.i2:"+s.i);//1 System.out.println("s.j2:"+s.j);//1 } private Singleton() { System.out.println("i0:"+i);//i:0 System.out.println("j0:"+j);//j:0 i++; j++; System.out.println("i:"+i);//i:1 System.out.println("j:"+j);//j:1 System.out.println("s.i:"+s.i);//1 System.out.println("s.j:"+s.j);//1 } public static Singleton get() { System.out.println("i1:"+i);//i1:1 System.out.println("j1:"+j);//j1:1 System.out.println("s.i1:"+s.i);//1 System.out.println("s.j1:"+s.j);//1 return s; } static { System.out.println("s.i3:"+s.i);//1 System.out.println("s.j3:"+s.j);//1 } } /*i0:0 j0:0 i:1 j:1 s.i:1 s.j:1 s.i2:1 s.j2:1 s.i3:1 s.j3:1 i1:1 j1:1 s.i1:1 s.j1:1 1 1 */
类加载器:
- 虚拟机自带类加载器
1.1根类加载器(Bootstrap)
1.2扩展类加载器(Extension)
1.3系统(应用)类加载器(System,App)
- ClassLoader的子类
Loader1发起加载Sample类,但是根加载器和扩展加载器都是加载特定目录的类,因此最终是由系统加载器加载成功,并把结果返回给loader1加载器。首先是向上委托到根,加载不了又向下委托,一个双向循环。
rt.jar是使用最多的类,平时使用的都是这个里面的。
定义类加载器:加载这个类成功的类加载器。Sample类是由系统类加载器加载成功的,系统类加载器就是定义类加载器。
package com.ssss; public class TT { public static void main(String[] args) throws Exception { Class cl = Class.forName("java.lang.String"); System.out.println(cl.getClassLoader());//null。返回类加载器,如果是null就是bootStrap类加载器加载的。 Class cl1 = Class.forName("com.ssss.C");//反射,主动使用,static会执行 System.out.println(cl1.getClassLoader());//sun.misc.Launcher$AppClassLoader@73d16e93,,内部类,系统加载器/应用加载器。 //应用加载器 加载的是 工程环境变量 classpath里面的类。 System.out.println("-------------------------------"); ClassLoader l = ClassLoader.getSystemClassLoader();//系统加载器 Class dd = l.loadClass("com.ssss.C");//只是loadClass,不是对类的主动使用,不是初始化,不执行static代码块 System.out.println(dd);//class com.ssss.C System.out.println("=============================="); ClassLoader l1 = l.getParent(); System.out.println(l1);//sun.misc.Launcher$ExtClassLoader@15db9742 ClassLoader l2 = l1.getParent(); System.out.println(l2);//null } } class C { static { System.out.println("ccccc"); } }
public class TT { public static void main(String[] args) throws Exception { ClassLoader t = Thread.currentThread().getContextClassLoader(); System.out.println(t);//sun.misc.Launcher$AppClassLoader@73d16e93。加载应用的加载器。 String s = "com\\ssss\\C.class"; Enumeration<URL> urls = t.getResources(s); while(urls.hasMoreElements()) { URL u = urls.nextElement(); System.out.println(u);//"com\\ssss\\C.class" } System.out.println("----=======-------========-----======="); Class c = TT.class; System.out.println(c.getClassLoader());//sun.misc.Launcher$AppClassLoader@73d16e93 Class c1 = String.class; System.out.println(c1.getClassLoader());//null。根加载器。String在rt.jar中 } }
类加载器加载的是类,不是对象。线程上下文clssloader是AppClassLoader