当通过java命令运行一个java程序的时候就启动了一个java虚拟机进程,这个进程会在以下情况下中止:
1.正常结束
2.出现异常和错误
3.程序中调用System.exit()
4.操作系统出现异常
当有两个程序在运行时一定是两个进程在执行,一个程序结束这能说明一个虚拟机进程结束而不是所有的进程结束。
当程序使用到一个类时java虚拟机需要先加载这个类,加载后的最终产品是该类的Class对象。加载过程分三个步骤,如下:
1.加载:(此处的”加载“是上面说的加载的第一步)
把类的.class文件中的二进制数据读入到内存,.class可能来自本地系统、下载的jar报中的class、专有数据库中提取.class 文件。进程会把二进制数据存放在运行时的数据区的
方法去内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区的数据结构,并且向java程序提供了访问类在方法去内的数据结构的接口,如下图:
类的加载是由类的加载器完成的,分为两种:
1)java虚拟机自带的类的加载器,包括启动类的加载器,扩展类加载器,系统类加载器
2)用户自定义类加载器,即继承java.lang.ClassLoader类,用户通过覆盖父类方法来定制类的加载方式。
后面会详细介绍类加载器。
类的加载的时机不同于类的初始化,类的初始化是在程序“首次主动使用它”才会加载。而类的加载并不需要类的“首次主动使用”才会加载,“首次主动使用”会在下面说明
java虚拟机会在预料到某个类预先使用它的时候预先加载该类,如果在预先加载的时候发现.class错误或者.class文件缺失,并不会报告错误,只有等到类主动使用该类时
2.连接包括以下过程
1)验证:确保被加载类的正确性。虽然在加载的时候已经验证,但虚拟机并不知道这个class到底是由谁创建的,是程序员还是黑客,经过验证可以保证程序的健壮性和
安全性
1)类文件结构检查:检查.class的文件的格式
2)语义检查:是否符合java语法规定,比如final类型不能覆盖和不能有子类。
3)字节码流检查:字节码流代表类的静态方法和实例方法,字节码流是由一组单字节指令组成的,这个指令叫操作码,每个操作码后面有一个或多个操作数,需要
检查是否有合法的操作数。
4)二进制兼容的验证:检查相互引用的类是否一致,例如A类一个方法调用B类的方法,他会查看内存中
方法区是否存在B类那个方法的字节码
2)准备:为类的静态变量分配内存,并将其初始化为默认值,例如int类型,为他分配空间,并且默认值为0
3)解析:把类中的符号引用转换为直接引用。如A类有个方法内有B.car()这个方法,那么解析就是将car() 这个描述符替换为指针,指向内存方法去中car()这个字节码
的内存地址
3.初始话:
给类的静态变量赋予正确的值,可以显示赋值,也可以在static{}中为静态变量赋值。如果一个类有父类就先初始化父类的静态变量,在任何初始化之前一定是先加载和连接该类
。只有在程序首次主动使用类时,类的初始化才会执行。”首次主动执行“包括六种动作,也就是说初始化在以下六种动作才会被执行。
1)创建类的实例:包括new,反射,克隆,反序列化创建
2)调用类的静态方法
3)访问类或接口的静态变量或为他赋值
4)初始化子类
5)调用java api 某些反射方法,如Class.forName("work"),如果work类还没有初始化,那么该操作会初始化他,并创建work类的Class实例
6)java虚拟机启动时表明启动类的类,如java sample命令,该命令启动sample,会初始化他
说明:
1.如果是final类型的静态变量,调用他并不一定会初始化该类,这要看final类型的变量在类加载连接的时候是否可以计算出他的值,如果能,那么就不会触发初始化该类,反之
。例如 final staitc int a = 5+9 ;这个在连接时可以确定a的值并为他分配空间。但如果是 final static int a = Math.random()+9就不能确定,这个值需要在运行的时候确
定,方法的返回值是在运行时确定的。所以会初始化该类。
2.当类初始化的时候一定要先初始化他的父类,但如果是初始化接口却不会初始化他的父接口。只有调用该接口的静态变量时才会导致该接口的初始化。如果调用子类中属于父类的
静态方法和静态变量不会导致子类的初始化。
3.如果调用ClassLoader类的loadClass()方法加载类,不会导致初始化,但会加载该类。只有显示调用Class.forName()的时候才算是主动使用他,才会初始化
1.正常结束
2.出现异常和错误
3.程序中调用System.exit()
4.操作系统出现异常
当有两个程序在运行时一定是两个进程在执行,一个程序结束这能说明一个虚拟机进程结束而不是所有的进程结束。
当程序使用到一个类时java虚拟机需要先加载这个类,加载后的最终产品是该类的Class对象。加载过程分三个步骤,如下:
1.加载:(此处的”加载“是上面说的加载的第一步)
把类的.class文件中的二进制数据读入到内存,.class可能来自本地系统、下载的jar报中的class、专有数据库中提取.class 文件。进程会把二进制数据存放在运行时的数据区的
方法去内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区的数据结构,并且向java程序提供了访问类在方法去内的数据结构的接口,如下图:
类的加载是由类的加载器完成的,分为两种:
1)java虚拟机自带的类的加载器,包括启动类的加载器,扩展类加载器,系统类加载器
2)用户自定义类加载器,即继承java.lang.ClassLoader类,用户通过覆盖父类方法来定制类的加载方式。
后面会详细介绍类加载器。
类的加载的时机不同于类的初始化,类的初始化是在程序“首次主动使用它”才会加载。而类的加载并不需要类的“首次主动使用”才会加载,“首次主动使用”会在下面说明
java虚拟机会在预料到某个类预先使用它的时候预先加载该类,如果在预先加载的时候发现.class错误或者.class文件缺失,并不会报告错误,只有等到类主动使用该类时
2.连接包括以下过程
1)验证:确保被加载类的正确性。虽然在加载的时候已经验证,但虚拟机并不知道这个class到底是由谁创建的,是程序员还是黑客,经过验证可以保证程序的健壮性和
安全性
1)类文件结构检查:检查.class的文件的格式
2)语义检查:是否符合java语法规定,比如final类型不能覆盖和不能有子类。
3)字节码流检查:字节码流代表类的静态方法和实例方法,字节码流是由一组单字节指令组成的,这个指令叫操作码,每个操作码后面有一个或多个操作数,需要
检查是否有合法的操作数。
4)二进制兼容的验证:检查相互引用的类是否一致,例如A类一个方法调用B类的方法,他会查看内存中
方法区是否存在B类那个方法的字节码
2)准备:为类的静态变量分配内存,并将其初始化为默认值,例如int类型,为他分配空间,并且默认值为0
3)解析:把类中的符号引用转换为直接引用。如A类有个方法内有B.car()这个方法,那么解析就是将car() 这个描述符替换为指针,指向内存方法去中car()这个字节码
的内存地址
3.初始话:
给类的静态变量赋予正确的值,可以显示赋值,也可以在static{}中为静态变量赋值。如果一个类有父类就先初始化父类的静态变量,在任何初始化之前一定是先加载和连接该类
。只有在程序首次主动使用类时,类的初始化才会执行。”首次主动执行“包括六种动作,也就是说初始化在以下六种动作才会被执行。
1)创建类的实例:包括new,反射,克隆,反序列化创建
2)调用类的静态方法
3)访问类或接口的静态变量或为他赋值
4)初始化子类
5)调用java api 某些反射方法,如Class.forName("work"),如果work类还没有初始化,那么该操作会初始化他,并创建work类的Class实例
6)java虚拟机启动时表明启动类的类,如java sample命令,该命令启动sample,会初始化他
说明:
1.如果是final类型的静态变量,调用他并不一定会初始化该类,这要看final类型的变量在类加载连接的时候是否可以计算出他的值,如果能,那么就不会触发初始化该类,反之
。例如 final staitc int a = 5+9 ;这个在连接时可以确定a的值并为他分配空间。但如果是 final static int a = Math.random()+9就不能确定,这个值需要在运行的时候确
定,方法的返回值是在运行时确定的。所以会初始化该类。
2.当类初始化的时候一定要先初始化他的父类,但如果是初始化接口却不会初始化他的父接口。只有调用该接口的静态变量时才会导致该接口的初始化。如果调用子类中属于父类的
静态方法和静态变量不会导致子类的初始化。
3.如果调用ClassLoader类的loadClass()方法加载类,不会导致初始化,但会加载该类。只有显示调用Class.forName()的时候才算是主动使用他,才会初始化