0%

面试题-虚拟机知识

1、对象的内存布局

点击看答案

分为3个区域:对象头、实例数据 以及 对齐填充

对象头:包括两部分信息,第一部分:对象自身运行时数据,如hashcode、GC年龄分代、锁状态标志位,官方称为”Mark Word”。第二部分:类型指针,虚拟机通过这个指针确定对象是哪个类的实例。

实例数据:对象真正存储的有效信息。

对齐填充:比如HotSpot vm 要求对象起始地址必须是8的整数倍。对齐填充不是必需的。

以下是句柄访问 时,内存布局关系图:

内存布局关系图

以上内容参考自: 对象创建与定位

2、垃圾收集算法

点击看答案
  • 标记-清除 算法。不足:1、标记清除效率不高。2、产生内存碎片。
  • 复制算法。为了解决标记清除的效率问题,将内存划分为大小相等的两块,每次使用一块。不足:可用内存缩小为原来一半。
  • 标记-整理。不足:复制存货对象耗时过多。
  • 分代收集算法。 新生代使用复制算法;老年代采用“标记-清除” 或者 “标记-整理”算法。

以上内容参考自: 垃圾回收算法

3、说说四大引用?强,软,弱,虚,并说明下合适GC

点击看答案

强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。

软引用是用来描述一些有用但并不是必需的对象。对于软引用关联着的对象,只有在内存不足的时候 JVM 才会回收该对象。这一点可以很好地用来解决 OOM 的问题,并且这个特性很适合用来实现缓存:比如网页缓存、图片缓存等。

弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。

“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。要注意的是,虚引用必须和引用队列关联使用.

4、Android中内存分配的执行流程

点击看答案

铺垫

Dalvik 虚拟机的Java堆的底层实现是一块共享匿名内存(Ashmem),并且将其抽象为C库的一个 mspace ,于是,Dalvik 虚拟机就可以利用 C 库里面的 dlmalloc 内存分配器来解决内存碎片问题(这是个成熟的内存分配器,可以很好地解决内存碎片的问题)

Android中内存分配的执行流程:

流程图如下:

dalvik虚拟机内存分配流程

  1. 尝试在Java堆上分配指定大小的内存,如果内存充足,就直接分配成功。(不改变java堆当前大小的前提下进行内存分配,属于轻量级的内存分配)
  2. 如果分配失败,就执行一次GC(如果此时有GC在运行,则等待这次GC执行完成),GC 时设置参数false标记不要回收软引用的对象。
  3. GC 完成后,再次尝试轻量级内存分配操作,如果内存充足,就分配成功了
  4. 如果上一步内存分配失败,就考虑将Java堆的当前大小设置为Dalvik 虚拟机启动时指定的Java堆最大值,再进行内存分配
  5. 如果内存充足,就完成内存的分配
  6. 如果上一步还是分配失败,就再次调用GC,并将参数标记为true,表示要回收软引用的对象
  7. GC完毕后,再次尝试分配,成功就返回;不成功也就抛出OOM了

以上内容参考自:老罗的博客

5、Android GC 流程

Art 虚拟机GC过程

点击看答案

Art的gc流程

由图可知,非并行GC的过程如下所示:

  1. 挂起所有的ART运行时线程。
  2. 调用子类实现的成员函数MarkingPhase执行GC标记阶段。
  3. 调用子类实现的成员函数ReclaimPhase执行GC回收阶段。
  4. 恢复第2步挂起的ART运行时线程。

并行GC的过程如下所示:

  1. 获取用于访问Java堆的锁。
  2. 调用子类实现的成员函数MarkingPhase执行GC并行标记阶段。
  3. 释放用于访问Java堆的锁。
  4. 挂起所有的ART运行时线程。
  5. 调用子类实现的成员函数HandleDirtyObjectsPhase处理在GC并行标记阶段被修改的对象。。
  6. 恢复第4步挂起的ART运行时线程。
  7. 重复第5到第7步,直到所有在GC并行阶段被修改的对象都处理完成。
  8. 获取用于访问Java堆的锁。
  9. 调用子类实现的成员函数ReclaimPhase执行GC回收阶段。
  10. 释放用于访问Java堆的锁。

从上面的分析就可以看出,并行GC和非并行GC的区别在于:

  1. 非并行GC的标记阶段和回收阶段是在挂住所有的ART运行时线程的前提下进行的,因此,只需要执行一次标记。
  2. 并行GC的标记阶段只锁住了Java堆,因此它不能阻止那些不是正在分配对象的ART运行时线程同时运行,而这些同进运行的ART运行时线程可能会引用了一些在之前的标记阶段没有被标记的对象。如果不对这些对象进行重新标记的话,那么就会导致它们被GC回收,造成错误。因此,与非并行GC相比,并行GC多了一个处理脏对象的阶段。所谓的脏对象就是我们前面说的在GC标记阶段同时运行的ART运行时线程访问或者修改过的对象。
  3. 并行GC并不是自始至终都是并行的,例如,处理脏对象的阶段就是需要挂起除GC线程以外的其它ART运行时线程,这样才可以保证标记阶段可以结束。

Dalvik 虚拟机垃圾收集过程

点击看答案

Dalvik 虚拟机使用 Mark-Sweep 算法来进行垃圾收集
Dalvik 执行GC 时会有一些选项:

  • isPartial,为true 时,表示仅仅回收Active 堆的垃圾;为false时,表示同时回收Active 堆和Zygote 堆的垃圾
  • isConcurrent:为true时,表示执行并行GC,false时,表示非并行GC
  • doPreserve:为true的时候,表示不回收软引用的对象;false的时候,表示回收软引用对象
    Dalvik在如下几种情况会触发GC:
  • 分配对象时,内存不足触发GC
  • 已经分配的内存达到一定阈值时触发GC
  • 调用 System.gc 、VMRuntime.gc 或者收到信号触发 GC
  • 准备抛出 OOM 前而最后进行的 GC
    GC线程在空闲达到一定时间后,会调用函数对Java堆进行裁剪,将一些没有用到的内存交还给内核。
    dalvik 的GC 流程(并行和非并行情况)如下图所示:
    dalvik虚拟机gc流程

哪些对象可以做 GC Root

  • 虚拟机栈引用的对象
  • 本地方法栈引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • Dalvik 虚拟机内部创建的部分异常对象
  • Dalvik 虚拟机内部创建的原子类
  • 注册在调试器的对象

以上内容参考自以上内容参考自 罗升阳的博客老罗的博客

6、如何理解Java类加载机制

谢谢你的鼓励