1.jdk为什么不在解释运行时直接解释源代码,而是字节码。 理论上,完全可以直接解释源码,这样也可以跨平台。而引入字节码有额外的好处:

  • 直接执行字节码,比解释源码再执行,会更快。
  • 生成字节码过程中,编译器可以预先作语法错误或者安全性方面的检查,出错机会更少。
  • 字节码比源码更加紧凑,文件尺寸更小,方便网络传输。
  • 有些嵌入设备,不够资源跑起完整的编译器,这些设备只需要嵌入一个小巧的JVM就行了,在额外的平台上编译源码。
  • 字节码不一定非要java源码生成,其它一些语言比如scala也可以编译生成字节码。这样其它语言就可以利用上经过多年发展的JVM。

2.java是静态语言还是动态语言,动态语言的好处。

  • 静态语言是在编译时变量的数据类型即可确定的语言,多数静态类型语言要求在使用变量之前必须声明数据类型。
  • 动态语言是在运行时确定数据类型的语言。变量使用之前不需要类型声明,通常变量的类型是被赋值的那个值的类型。
  • 静态语言的优势
优点:  
      结构非常规范,便于调试,方便类型安全
      由于类型的强制声明,使得IDE有很强的代码感知能力,在实现复杂的业务逻辑、开发大型商业系统、以及那些生命周期很长的应用中,依托IDE对系统的开发很有保障
      由于静态语言相对比较封闭,使得第三方开发包对代码的侵害性可以降到最低

  缺点:
      为此需要写更多类型相关代码,不便于阅读、不清晰明了
      思维不受束缚,可以任意发挥,把更多的精力放在产品本身上
      集中思考业务逻辑实现,思考过程即实现过程
复制代码
  • 动态语言的优势
优点:方便阅读,不需要写非常多的类型相关的代码;

 缺点:不方便调试,命名不规范时会造成读不懂,不利于理解等
复制代码

3.jvm启动流程

  • 1、配置JVM装载环境
JVM.dll文件的查找和装载
复制代码
  • 2、解析虚拟机参数
  • 3、设置线程栈大小
  • 4、执行Java main方法
CreateJavaVM 新建JVM实例
  加载主类的class
  查找main方法
  执行main方法
复制代码

4.jvm优缺点

  • 非常经济地实现跨平台。语言编译器只需要输出 JVM 字节码就可以。跨平台需要极大的工作量,举个例子,只是独立开发生成本地代码,就需要花费大量精力去针对不同平台和处理器进行优化 。
  • JVM 卓越的 JIT (Just-In-Time 即时编译)性能。 JIT 可以在运行中记录程序运行的特征,并在其基础上做大量的优化(Java 企业级应用的优秀性能很大程度上是由此而来)。 JIT 自从 HotSpot JVM 随 Java 1.2 发布以来,JVM JIT 的性能不断提高,是无可争议的成功产品。把 JVM 作为目标平台意味着大量的性能优化工作可以「外包」给 JVM 来做,大大缩减了 Guest 语言的开发预算。
  • 已经有多个成熟的实例,有大量的经验可以借鉴
  • JVM 作为一个成熟的高层运行环境,为 Guest 语言提供了很多运行时所需要的服务,比如内存管理(有业界领先的垃圾回收等),很大程度上避免了额外的独立开发。
  • JVM 有多个独立实现,也有若干厂商会持续推进,资料完备,社区巨大。
  • Java 社区有大量成熟的库,一般来说,运行在 JVM 上的其它语言都会设计一个专用的「桥」来帮助直接使用 Java 的库,对潜在客户来说是个很好的卖点。
  • Java 有还算不错的开发工具和环境。目标为 JVM 的很多语言会考虑用 Java 语言实现(至少在 bootstrap 阶段)。

5.JIT即时编译的作用,热点代码探测技术。

运行次数超过某个阀值的代码 运行过程中会被即时编译器编译的“热点代码”有两类:

  • 1、被多次调用的方法。
  • 2、被多次执行的循环体。

两种情况,编译器都是以整个方法作为编译对象。
这种编译方法因为编译发生在方法执行过程之中,因此形象的称之为栈上替换(On Stack Replacement,OSR),即方法栈帧还在栈上,方法就被替换了

目前主要的热点探测方式有以下两种:
(1)基于采样的热点探测 采用这种方法的虚拟机会周期性地检查各个线程的栈顶,如果发现某些方法经常出现在栈顶,那这个方法就是“热点方法”。这种探测方法的好处是实现简单高效,还可以很容易地获取方法调用关系(将调用堆栈展开即可),缺点是很难精确地确认一个方法的热度,容易因为受到线程阻塞或别的外界因素的影响而扰乱热点探测。
(2)基于计数器的热点探测 采用这种方法的虚拟机会为每个方法(甚至是代码块)建立计数器,统计方法的执行次数,如果执行次数超过一定的阀值,就认为它是“热点方法”。这种统计方法实现复杂一些,需要为每个方法建立并维护计数器,而且不能直接获取到方法的调用关系,但是它的统计结果相对更加精确严谨。




如何编译为本地代码? Server Compiler和Client Compiler两个编译器的编译过程是不一样的。 对Client Compiler来说,它是一个简单快速的编译器,主要关注点在于局部优化,而放弃许多耗时较长的全局优化手段。 而Server Compiler则是专门面向服务器端的,并为服务端的性能配置特别调整过的编译器,是一个充分优化过的高级编译器。

6、为什么Hotspot使用解释器和即时编译器共存

解释器我们可以理解为,把一种高级语言转换成另一种语言的程序。在JVM中,解释器即将字节码文件转成机器二进制语言,使我们的电脑可以直接执行。然而因为每次运行程序时都要先转成另一种语言再作运行,因此解释器的运行速度可想而知,这也造成了java运行速度比较慢的印象。

尽管并不是所有的Java虚拟机都采用解释器与编译器并存的架构,但许多主流的商用虚拟机(如HotSpot),都同时包含解释器和编译器。解释器与编译器两者各有优势:当程序需要迅速启动和执行的时候,解释器可以首先发挥作用,省去编译的时间,立即执行。在程序运行后,随着时间的推移,编译器逐渐发挥作用,把越来越多的代码编译成本地代码之后,可以获取更高的执行效率。当程序运行环境中内存资源限制较大(如部分嵌入式系统中),可以使用解释器执行节约内存,反之可以使用编译执行来提升效率。此外,如果编译后出现“罕见陷阱”,可以通过逆优化退回到解释执行。

因为如果Java虚拟机只能够在运行时对代码采用逐行解释执行,程序的运行性能可想而知。但是如今的HotSpot VM中不仅内置有解释器,还内置有先进的JIT(Just In Time Compiler)编译器,在Java虚拟机运行时,解释器和即时编译器能够相互协作,各自取长补短。在此大家需要注意,无论是采用解释器进行解释执行,还是采用即时编译器进行编译执行,最终字节码都需要被转换为对应平台的本地机器指令。或许有些开发人员会感觉到诧异,既然HotSpot VM中已经内置JIT编译器了,那么为什么还需要再使用解释器来“拖累”程序的执行性能呢?比如JRockit VM内部就不包含解释器,字节码全部都依靠即时编译器编译后执行,尽管程序的执行性能会非常高效,但程序在启动时必然需要花费更长的时间来进行编译。对于服务端应用来说,启动时间并非是关注重点,但对于那些看中启动时间的应用场景而言,或许就需要采用解释器与即时编译器并存的架构来换取一个平衡点。

解释器的执行,抽象的看是这样的:

输入的代码 -> [ 解释器 解释执行 ] -> 执行结果  
复制代码

而要JIT编译然后再执行的话,抽象的看则是:

输入的代码 -> [ 编译器 编译 ] -> 编译后的代码 -> [ 执行 ] -> 执行结果
复制代码

7.JVM结构

JVM = 类加载器 classloader + 执行引擎 execution engine + 运行时数据区域 runtime data area
复制代码



其中数据区域



堆区:

1.存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令)

    2.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身
复制代码

栈区:

1.每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中
    2.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
    3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
复制代码

方法区:

1.又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
    2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。
复制代码

8.字节码包含哪些数据

http://www.importnew.com/24088.html
    3.1 魔数
    3.2 版本号
    3.3 常量池
    3.4 Access_Flag 访问标志
    3.5 类索引
    3.6父类索引
    3.7 接口索引
    3.8 字段表集合
    3.9 方法
复制代码

9.字节码加载流程

http://www.importnew.com/18548.html
    加载->链接(验证+准备+解析)->初始化(使用前的准备)->使用->卸载
复制代码

首先是加载,这一块虚拟机要完成3件事:

1.通过一个类的全限定名来获取定义此类的二进制字节流。
2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3.在java堆中生成一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口。
复制代码

检验主要经历几个步骤:

文件格式验证->元数据验证->字节码验证->符号引用验证
复制代码

文件格式验证:验证字节流是否符合Class文件格式的规范并 验证其版本是否能被当前的jvm版本所处理。ok没问题后,字节流就可以进入内存的方法区进行保存了。后面的3个校验都是在方法区进行的。
元数据验证:对字节码描述的信息进行语义化分析,保证其描述的内容符合java语言的语法规范。
字节码检验:最复杂,对方法体的内容进行检验,保证其在运行时不会作出什么出格的事来。
符号引用验证:来验证一些引用的真实性与可行性,比如代码里面引了其他类,这里就要去检测一下那些来究竟是否存在;或者说代码中访问了其他类的一些属性,这里就对那些属性的可以访问行进行了检验。

准备阶段:为静态变量设置初始值

解析:解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。

初始化: 执行程序的<clinit>(),<clinit>()方法叫做类构造器方法 赋值