虚拟机是有规范的,HotSpotsun公司也就是现在的oracle公司提供的,虚拟机并不是只有oracle有,IBM也有虚拟机,虚拟机是有规范的, 只要遵循这个规范就可以自己造虚拟机。虚拟机也有商用的,收费很高。Oracle是免费的虚拟机。Openjdk是基于jdk的开源实现,open是开源的意思。

HotSpotoracle的,是使用的很广泛的。

 

Java代码中,类型的加载,链接,初始化都是在程序运行期间runtime完成的(其他的一些语言是编译期间完成的)。类型是定义的一个类、接口、枚举,不是对象。

 

动态代理是类型在运行期间动态的生成出来,在编译期间没有的。

 

Java是静态语言,但是也有很多动态的特性。

 

 

 

加载:class文件从磁盘到内存

 

连接:类与类的关系确定好,字节码的验证校验。字节码有问题虚拟机就不会去执行。字节码文件是可以人为操纵的。

 

初始化:类的静态变量赋值。都是在运行期间完成的,不是编译期间。

 

 

 

类加载器:每一个类型(类)会被纳入jvm管辖范围中。加载类的工具。

 

虚拟机就是一个进程,进程在某些情况下就会停掉:

 

  1. 程序执行了System.exit()
  2. 程序正常结束
  3. 执行过程中遇到了异常
  4. 操作系统层面出现错误导致虚拟机进程结束

 

符号引用就是字符串,写的时候,直接引用就是去找相应的地址

 

 

 

类的卸载:类的信息从内存移除。

JVM1_类加载器

加载:类加载到内存,.class文件二进制数据读入到内存,放入到方法区中,然后在内存创建一个Class对象,虚拟机规范并没有规定Class对象放在哪里,OracleHotSpot虚拟机将其放在了方法区中(没有放到堆区),Class对象封装类在方法区的数据结构。所有的实例都会对应一份Class对象。所以反射要使用Class对象。

验证:验证类的正确性

准备:静态变量赋予初始值,分配相应的空间大小。

解析:符号引用转换为直接引用

初始化:赋予正确的初始值

加载链接初始化加载到内存、校验,赋予默认值、正确值,符号引用转换

加载class文件的方式:

  1. 磁盘直接加载,位于classpath类路径下
  2. 网络下载.class文件
  3. Zip,jar文件加载.class文件
  4. 数据库提取.class文件(使用的少)
  5. Java源文件动态编译为.class文件(动态代理)。运行期生成,编译时没有。jsp文件转换成class文件。

 

Java对类的使用方式:

 

  1. 主动使用
  2. 被动使用

 

类或接口必须首次主动使用初始化(初始化就会执行里面的static代码块)。不首次主动使用也会加载,只是不初始化

 

JVM规范允许加载器预料某个类将要使用时候预先加载它,如果预先加载中遇到了class文件错误,类加载器要在首次主动使用这个类时候报告错误。入股这个类一直没有首次使用,就一直不报错

 

主动使用7种:

 

  1. 创建实例
  2. 访问(读写)类的静态变量。调用类的静态方法
  3. 反射
  4. 初始化类的子类(初始化之类时候父类也是主动使用,也会初始化,爷爷类也会初始化)
  5. 被标记为启动类的类(main函数了类,入口类)

 

其他使用类的方式都是被动使用,都不会导致类的初始化。有可能导致类的加载和链接

JVM1_类加载器_02

调用ClassLoader类的loadClass方法加载一个类,并不是对类的主动使用,不会导致类的初始化。

JVM1_初始化_03

 

先让父类加载器加载,加载不了在自己加载,是为了安全

JVM1_加载_04

JVM1_加载_05

表面看是一种继承关系,实际上是一种包含关系。

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的参数有上千个,

JVM1_加载器_06

 

如何在windows平台下使用hsdisjitwatch查看JIT后的汇编码

javascript:void(0)

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
*/

JVM1_加载器_07

类加载器:

  1. 虚拟机自带类加载器

1.1根类加载器(Bootstrap

1.2扩展类加载器(Extension

1.3系统(应用)类加载器(System,App

  1. ClassLoader的子类

JVM1_加载_08

 

Loader1发起加载Sample类,但是根加载器和扩展加载器都是加载特定目录的类,因此最终是由系统加载器加载成功,并把结果返回给loader1加载器。首先是向上委托到根,加载不了又向下委托,一个双向循环

JVM1_初始化_09

 

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中
    }
}

类加载器加载的是类,不是对象线程上下文clssloaderAppClassLoader

JVM1_初始化_10