1. java虚拟机是继续栈的还是基于寄存器的?有没有基于寄存器的虚拟机?有什么差别
- java虚拟机是基于堆栈的,在java虚拟机栈中,每个方法执行的同时,会创建一个栈帧
- 栈帧,用于储存局部变量表,动态链接,操作数栈,方法返回地址。
- jvm是基于堆栈的,而dalvik虚拟机是基于寄存器的,基于堆栈的指令占用空间小,一个指令常常占用一个字节,所以叫做字节码,而dalvik由于需要指定源地址和目标地址,所以指令需要更多的空间
- 二者各有优劣,一般而言,执行同样的功能,前者需要更多的指令,也就是说需要占用更多的cpu时间,而后者需要需要更多指令空间意味着数据缓冲更易失效。
- 此外,还有一种观点认为,基于堆栈的指令更具可移植性,因为它不对目标机器的寄存器进行任何假设。然而,基于寄存器的指令由于对目标机器的寄存器进行了假设,因此,它更有利于进行AOT(ahead-of-time)优化。 所谓AOT,就是在解释语言程序运行之前,就先将它编译成本地机器语言程序。AOT本质上是一种静态编译,它是相对于JIT而言的,也就是说,前者是在程序运行前进行编译,而后者是在程序运行时进行编译。运行时编译意味着可以利用运行时信息来得到比较静态编译更优化的代码,同时也意味不能进行某些高级优化,因为优化过程太耗时了。另一方面,运行前编译由于不占用程序运行时间,因此,它就可以不计时间成本来优化代码。无论AOT,还是JIT,最终的目标都是将解释语言编译为本地机器语言,而本地机器语言都是基于寄存器来执行的,因此,在某种程度来讲,基于寄存器的指令更有利于进行AOT编译以及优化。
参考文献
方法区的作用
- 用于存储虚拟机加载的新信息, 常量,静态变量,及时编译器编译后的代码等数据。
- 运行时常量池,是方法区的一部分,
垃圾收集器和内存分配策略
怎样判断对象已死?
引用计数法
在jvm中没有选用引用计数法来管理内存,其原因是因为其很难解决对象之间相互引用的问题。
可达性分析算法 根搜索算法
以GC ROOT 的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链。
当一个对象到gc Root 没有任何引用链时,证明此对象时不可用的。
gc root 包含的对象
- 方法区,静态变量
- 方法区,常量引用的对象
- java虚拟机栈中引用的对象
- 本地方法栈中引用的对象。
四种引用
强引用
垃圾收集器永远不会回收掉被引用的对象。
软引用
将要发生内存溢出之前,将会把这些对象列进回收的范围中,进行第二次回收。如果回收之后,还是没有足够的内存,将会抛出内存溢出异常。
弱引用
被弱引用关联的对象只能活到下次内存回收之前。
垃圾收集算法
标记清除
缺点 产生大量的不连续的内存碎片
复制算法
缺点 对内存空间的利用不高,有一半的内存没有被利用,适用于朝生夕死的对象
标记整理
所有存活的对象向一边移动,然后清理到端边界以外的内存。
分代收集算法
一般将java堆分为新生代和老年代
每次垃圾收集都有大量对象死去就采用复制算法
而老年代中因为对象的存货率高,没有额外的空间,就需要使用标记清除或者标记整理算法。
虚拟机的类加载机制
加载 验证 准备 解析 初始化 使用 卸载
什么情况下开始类加载过程的第一个阶段:加载?
- 遇到new, getStatic putstatic 或者invockestatic这四个字节码指令时,如果类没有进行初始化,则先触发其初始化,使用new 关键字实例化的时候,读取或设置一个类的静态字段(被final修饰,已经编译把结果放入常量池的静态字段除外。)
- 使用java.lang.reflect包的方法对类进行反射调用的时候。如果类没有进行初始化,则先出发初始化。
- 当初始化一个类的时候,如果发现其父类,还没有进行初始化,则需要先触发其父类的初始化。
- 当虚拟机启动时,用户需要指定一个要执行的主类(包含main()的那个主类)虚拟机先初始化这个主类。
- 当使用jdk1.7的动态语言支持支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic,REF_putStatic,REF_invokeStatic.
准备
准备阶段是正式为类变量分配内存并设置类初始值的阶段。
- 内存分配的仅包括类变量(被static修饰的变量),而不包括实例变量,实例变量会在对象实例化时随着对象一起分配到java堆中。
初始化
- 到了初始化阶段,才真正执行类中定义的java程序代码(字节码)。
- 在初始化阶段,根据程序员通过制定的主观计划去初始化变量和其他 资源。
- 初始化阶段是执行类构造器()方法的过程。
类加载器
- 从java虚拟机来讲,只存在两种不同的类加载器:一种是启动类加载器,另一种是其他的类加载器。
- 如图展示的类加载器之间的这种层次关系,称为类加载器的双亲委派模型,双亲委派模型要求除了顶层的启动类加载器之外,其余的类加载器都应有自己的父类加载器。这里类加载器之间的父子关系一般不会以继承的关系实现,而是都使用组合关系来复用父加载器的代码。
- 双亲委派模型的工作过程:如果一个类加载器收到类加载的请求,首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此。因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有父类加载器反馈自己无法完成这个加载请求时,子加载器 才会尝试自己去加载。
- 双亲委派模型的好处: java类随着他的类加载器一起具备了一种带有优先级的层次关系。
参考文献
- 《深入理解java虚拟机》