概述
1、JVM DVM ART的区别
JVM是java语言编译代码的运行平台,JVM结构包括运行时数据区、执行引擎、本地方法库和本地方法接口组成。DVM是Dalvik虚拟机,是Google专门为Android平台开发的虚拟机,DVM并不是JVM,因为它没有遵循JVM的规范。DVM与JVM的区别在于:
(1)基于的架构不同。JVM是基于栈的,拷贝数据时需要大量的出入栈的指令和更多的内存访问次数,这会导致速度更慢,所以JVM并不适合性能有限的移动设备。而DVM是基于寄存器的,不需要在复制数据时使用大量的出入栈指令,并且指令更加紧凑和简洁,但是由于DVM的指令需要显示指定操作数,指令会更大,但由于指令数量的减少,总的代码数不会增加多少。
(2)执行的字节码不同。java中类会被编译成一个或多个.class文件,每个.class文件中包含了类的相关信息,然后打包成.jar文件,之后JVM会根据相应的.class和.jar文件获取相应的字节码。党jvm加载.jar文件的时候,需要加载里面所有的.class文件,这种加载方式速度较慢,并不适合内存有限的移动设备。
DVM会通过dx工具将所有.class文件整合为一个.dex文件,还会去除.class文件里的冗余信息,然后从.dex文件中读取指令和数据,这样就减少了IO操作,提高了类的查找速度。
从Android5.0开始,ART就开始替代DVM,ART与DVM的区别在于:
DVM执行的是dex字节码,而ART执行的是本地机器码。DVM执行的是dex字节码,一般的代码是解释执行,热点代码会在运行时动态的编译成机器码,然后再执行。但是将dex字节码翻译成本地机器码的过程是在程序运行的时候,而且程序每一次重新运行的时候,都要重新编译一次。因此,就算采用了JIT,DVM的性能还是不能与直接执行本地机器码的ART相比。
在ART中,每次程序安装时会进行一次AOT,也就是将字节码预编译成机器码并存储在本地,这样程序执行时就不需要执行编译了,效率更高,启动更快,但是这样就增加了系统安装应用的时间,并且更占用内存。
2、描述GC机制。Class会不会回收?用不到的Class怎么回收
在JVM的运行时数据区里,程序计数器、虚拟机栈、本地方法栈是线程私有的,和线程的生命周期相同。程序计数器就是一小块内存空间,线程创建时就已经确定。栈中的栈帧分配的内存基本上在类结构确定下来时也是已知的,所以这几个区域都不用考虑内存回收的问题,方法结束或者线程结束,内存自然就被回收了。
而堆和方法区是线程共有的,只有程序运行时才知道需要创建多少对象,这部分的内存分配是不确定的,GC回收关注的就是这两个区域。
而判断对象能否被回收,有两个算法:引用计数法和可达性分析算法,引用计数法无法解决循环引用的问题,有一定缺陷,目前采用的是可达性分析算法。可达性分析算法是指从GC root对象为起始点,根据引用关系向下搜索,搜索经过的路径称为引用链,如果某个对象和GC root对象之间没有通过引用链相连,那么这个对象就可以被回收。
GC root对象有:
a. 虚拟机栈中局部变量表引用的对象(正在执行的方法中局部变量指向的对象)
b. 方法区中静态引用指向的对象(类中static修饰的变量指向的对象)
c. 方法区中常量引用的对象
d. 仍处于存活状态中的线程对象
e. Native方法中JNI引用的对象
而无论是哪个引用算法,都与应用有关。JDK1.2以前,如果引用类型的数据的值是另一块内存的地址,就称这个引用数据为这块内存或者对象的引用,但是这太绝对了,一个对象只有被引用和没被引用两种状态,引用了就不被回收,没被引用就回收。如果我们想表示那些“内存不足时回收,内存充足时保留”的对象就没办法了。所以java对引用的概念进行了扩展,分为了四种引用:强引用、软引用、弱引用、虚引用,引用强度从左到右依次减弱。这样就可以让程序自己决定对象的生命周期,垃圾回收器即根据不同的引用进行不同的处理。
(1)强引用:Object obj = new Object(),把一个对象赋给一个变量,这个引用变量就是一个强引用。当一个对象被一个强引用变量引用时,除非超过了引用的作用域,否则是不可能被垃圾回收器回收的。
(2)软引用:SoftReference<Object> s = new SoftReference<Object>(obj),用来描述有用但非必需的对象,当内存不足的时候才会软引用引用的对象进行回收。一般用在对内存敏感的程序中,比如高速缓存。
(3)弱引用:WeakReference<Object> w = new WeakReference<Object>(obj),用来描述非必需对象,不管内存是否足够,都会对其进行回收。ThreadLocal中就存在着弱引用,ThreadLocal之后在Handler机制中再总结。
(4)虚引用:虚引用对对象的生命周期完全没有影响,如果一个对象被虚引用引用,那么就和没有引用一样,任何时候都可能被垃圾回收,不能单独使用,也不能通过虚引用操作对象(PhantomReference.get()总是返回null)。
GC回收的垃圾收集算法主要四种,目前采用的最多的是分代收集算法:
(1)标记-清除算法:通过可达性分析之后存活的对象保留,需要回收的对象进行标记,然后将其起始地址和结束地址放到一个空闲地址链表中,下一次内存分配的时候从这个链表中进行查找。这个方法速度快,但是容易产生内存碎片。
(2)标记-整理算法:通过可达性分析之后,将存活的对象都整理到内存的一端,并清理边界之外的空间。这个方法不会产生内存碎片,但是牵扯到对象的移动,地址的改变,速度慢。
(3)复制算法:将现有的内存空间分为两块,称为from区和to区。首先进行可达性分析,将from中存活的对象复制到to区,然后交换from和to。这个方法不会产生内存碎片,但是会占用双倍的内存空间。
(4)分代回收算法:新创建的对象在新生代中分配内存,此区域的对象一般生命周期较短,垃圾回收的效率也很高,一般采用的是复制算法。如果经过多次垃圾回收后依然存活,则晋升至老年代中。新生对分为伊甸园Eden区、from区和to区,新对象创建时默认采用伊甸园Eden中的空间,Eden空间不足的时候会触发minor gc,将垃圾对象回收清除,并将Eden和幸存区from中存活的对象复制到幸存区to中,并且对象年龄加1,然后交换from和to。 幸存区中的对象寿命如果超过阈值(默认15),就会晋升到老年代。如果新创建的对象比较大,并且新生代空间不足,也会直接放至老年代。而老年代的对象因为生命周期较长,不需要过多的复制操作,一般采用标记-整理算法。
Class存在方法区中,方法区的回收主要分为两部分:废弃的常量和不再使用的Class。判定一个常量是否废弃,和对象类似,只有这个常量不再被引用,就会被回收。而Class的回收则需要满足3个条件:
(1)该类的所有实例都已经被回收,也就是堆中不再存在该类及其所有子类的实例。
(2)加载该类的类加载器被回收。
(3)该类对应的Class对象没有被引用,无法在任何地方通过反射调用其方法
但是满足了这三个条件,不代表就一定被回收,只是说允许被回收,是否要回收,可以通过参数 -Xnoclassgc 进行控制。
3、StackOverFlow与OOM的区别?分别发生在什么时候,JVM栈中存储的是什么,堆存储的是什么
StackOverFlow是栈溢出,线程请求的栈深度大于虚拟机允许的深度,一般是递归调用引起的。OOM是Out of Memory,当堆中没有内存分配实例并且堆也无法扩展时,或者栈进行动态扩展时无法分配足够的内存时抛出的异常。
JVM栈中存储的是栈帧,栈帧是方法被调用时创建的,栈帧中包含的是局部变量表、操作数栈、动态链接、方法出口等信息,局部变量表中存储的是基本数据类型的局部变量和对对象的引用,不存储对象的内容。操作数栈可以理解为栈帧中用于计算的临时数据存储区。
JVM堆中存储的是new出来的对象,所以对象实例和数组都在堆上分配。
4、Java虚拟机和Dalvik虚拟机的区别
这个在上面的第一题已经说过了。
最后
以上就是紧张星星为你收集整理的Android实习面试准备——java基础(三)的全部内容,希望文章能够帮你解决Android实习面试准备——java基础(三)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复