java虚拟机基础知识大全 java虚拟机运行原理( 三 )


《Java虚拟机规范》则是严格规定了有且只有六种情况必须立即对类进行“初始化”(而加载、验证、准备自然需要在此之前开始):
1)遇到new、、或这四条字节码指令时,如果类型没有进行过初始化,则需要先触发其初始化阶段 。能够生成这四条指令的典型Java代码场景有:·使用new关键字实例化对象的时候 。·读取或设置一个类型的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)的时候 。·调用一个类型的静态方法的时候 。
2)使用java.lang.包的方法对类型进行反射调用的时候,如果类型没有进行过初始化,则需要先触发其初始化 。
3)当初始化类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化 。
4)当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类 。
5)当使用JDK 7新加入的动态语言支持时,如果一个java.lang..实例最后的解析结果为、、、四种类型的方法句柄,并且这个方法句柄对应的类没有进行过初始化,则需要先触发其初始化 。
6)当一个接口中定义了JDK 8新加入的默认方法(被关键字修饰的接口方法)时,如果有这个接口的实现类发生了初始化,那该接口要在其之前被初始化 。
接口与类真正有所区别的是前面讲述的六种“有且仅有”需要触发初始化场景中的第三种:当一个类在初始化时,要求其父类全部都已经初始化过了,但是一个接口在初始化时,并不要求其父接口全部都完成了初始化,只有在真正使用到父接口的时候(如引用接口中定义的常量)才会初始化 。
1)通过一个类的全限定名来获取定义此类的二进制字节流 。
2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构 。
3)在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口 。
获取类的二进制字节流的形式
·从ZIP压缩包中读取,这很常见,最终成为日后JAR、EAR、WAR格式的基础 。
·从网络中获取,这种场景最典型的应用就是Web。·运行时计算生成,这种场景使用得最多的就是动态代理技术,在java.lang..Proxy中,就是用了.()来为特定接口生成形式为“*$Proxy”的代理类的二进制字节流 。
·由其他文件生成,典型场景是JSP应用,由JSP文件生成对应的Class文件 。·从数据库中读取,这种场景相对少见些,例如有些中间件服务器(如SAP )可以选择把程序安装到数据库中来完成程序代码在集群间的分发 。
·可以从加密文件中获取,这是典型的防Class文件被反编译的保护措施,通过加载时解密Class文件来保障程序运行逻辑不被窥探 。
验证阶段大致上会完成下面四个阶段的检验动作:文件格式验证、元数据验证、字节码验证和符号引用验证 。
“停机问题”( )[插图],即不能通过程序准确地检查出程序是否能在有限的时间之内结束运行 。
正式为类中定义的变量(即静态变量,被修饰的变量)分配内存并设置类变量初始值的阶段 。
首先是这时候进行内存分配的仅包括类变量,而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在Java堆中 。其次是这里所说的初始值“通常情况”下是数据类型的零值,假设一个类变量的定义为:
int value=http://www.kingceram.com/post/123;
那变量value在准备阶段过后的初始值为0而不是123,因为这时尚未开始执行任何Java方法,而把value赋值为123的指令是程序被编译后,存放于类构造器()方法之中,所以把value赋值为123的动作要到类的初始化阶段才会被执行 。表7-1列出了Java中所有基本数据类型的零值 。