我是靠谱客的博主 背后大地,最近开发中收集的这篇文章主要介绍安卓面试总结(5)——Java 虚拟机 I,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

上一篇

安卓面试总结(4)——Java 多线程II

上一篇写了多线程、锁机制的内容,写的不是很详细,需要配合一些知识来回顾,但是看完后应该会有一个大概了吧。接下来的 JVM 有点复杂,可能安卓也用不到,但是能够说清楚的话,面试还是有一定优势吧,至少肚子里有东西,不会慌,有利于提高信心。

一、运行时数据区域

在这里插入图片描述
又是别人的图啊,这个很简洁明了。

  1. 程序计算器

    • 记录正在执行的虚拟机字节码指令的地址(执行本地方法时为空)
  2. Java 虚拟机栈

    • 储存局部变量表、操作数栈、常量池引用等信息
    • 从方法调用直至完成,对应一个栈帧在 JVM 栈中入栈过程(方法 -> 栈帧 -> 调用)
    • 异常:
      • StackOverflowError:线程请求深度操作最大值(16?)
      • OutOfMemaryError:栈动态扩展时无法满足足够内存
  3. 本地方法栈:为本地方法服务

  4. 堆:

    • 所有对象都在这里分配内存,是垃圾收集的主要区域

    • 堆不需要连续内存,并且可以动态增加其内存,失败时会 OutOfMemaryError

  5. 方法区:

    • 用于存放已被加载的类信息、常量、静态变量、即时编译的代码等数据(4个)
    • 不需要连续内存,可动态扩展,失败会异常
    • 垃圾回收主要为常量池和类的卸载(比较难实现)
  6. 运行时常量池:也是方法区的一部分

    • Class 文件中的常量池(编译器生成的字面量和符号引用)会在类加载后被放入这个区域
    • 除了在编译期生成的常量,还允许动态生出,如 String 类的 intern 函数
  7. 直接内存

    • JDK1.4 中引入 NIO 类,可以使用 Native 函数库直接分配堆外内存

二、垃圾收集

  1. 针对目标:堆和方法区,线程部分会随线程结束而消失(面试被问了,居然没记住)
  2. 判断一个对象是否可被回收
    • 引用计数法:
      • 为对象添加一个引用技术区,增加引用加一,引用失效减一,0 的时候可被回收
      • 两个对象循环引用时(互相持有),计数永远不为 0,无法被回收
    • 可达性分析算法(JVM使用):
      • 以 GC ROOTS 为起点进行搜索,可达的对象是存活的,不可达则可回收
      • GC ROOTS 包含:
        • 虚拟机栈中局部变量表中引用的对象
        • 本地方法栈中 JVM 中引用的对象
        • 方法区中类静态属性引用的对象
        • 方法区中的常量引用的对象

三、方法区的回收

  1. 方法区主要存放永久代对象(JDK1.8 后移到堆、内存),回收率低
  2. 主要是对常量池的回收和卸载(为避免内存溢出,大量使用反射和动态代理 JVM 需要类卸载)
    • 类卸载条件(不一定卸载)
      • 该类所有对象实例已被回收,堆中怒不存在该类任何实例
      • 加载该类的 classloader 已被回收
      • 该类对应 class 对象在任何地方未被引用,也就无法在任何地方通过反射访问该类

四、finalize()

当一个对象可被回收时,finalize() 会被执行,但有且只有一次(可以使用引用自救一次)

五、引用类型

  • 强引用:使用 new 创建新强引用
  • 软引用:SoftReference<T>,只有在内存不足时才会被回收
  • 弱引用:WeakReference<T>,如果一个对象所有引用都是弱引用,那么将被回收
  • 虚引用:PhamtomReference<T>,虚引用持有对象会在任何时候被回收(无法获取实例,仅仅在回收时得到通知)

六、垃圾收集算法

  1. 标记 - 清除
    • 标记阶段:检查是否为活对象,打上标记
    • 清除阶段:进行对象回收,取消标志位,还会合并相邻分块
    • 在分配时:搜索空闲链表中空间大于新对象 size 的 block 块,大于还会返回空闲块
    • 不足:
      • 标记和清除效率都不高
      • 会产生大量不连续内存碎片
  2. 标记 - 整理
    • 让所有存活的对象都向另一端移动,然后清除掉端边界以为的内存
    • 优点:不会产生内存碎片
    • 不足:需要移动大量对象,处理效率比较低
  3. 复制
    • 将内存划分为大小相等的两块,每次只使用其中一块,这一块内存用完了就将还存活的对象复制到另一块上面,然后再把使用过的内存空间进行一次清理。
  4. 分代收集
    • 根据对象存活周期将内存划分为几块,不同块采用适当的收集算法
    • 新生代:复制,老年代:标记清除or标记整理

七、垃圾收集器

  1. 概述

    • 主要记住 CMS 和 G1 就够了吧。

    • 新生代:G1、Serial(串行)、ParNew(并行)、Paraller Scavenge(可控吞吐量,优先吞吐量)

    • 老年代:G1、CMS、Serial old(串行)、Parallel old(并行)

  2. CMS 收集器:以获得最短回收时间为目标的收集器

    • 初始标记:stop world,速度很快,GC Roots直接关联
    • 并发标记:GC Roots Tracing,所有存活对象
    • 重新标记:stop world,修正并发期间变动(时间久),耗时比初始长,比并发短
    • 并发清除
  3. G1 收集器

    • 空间整合,采用标记整理算法(复制?),不会产生内存空间碎片
    • 可预测停顿(处理的是 region)
    • 内存布局改变,将整个 Java 堆划分为大小相等的独立区域 Region
    • 还有新生代、老年代概念,但不是物理隔离了,收拾 Region 的集合
    • 和 ParNew 类似,当新生代达到一定比例时收集,和 CMS 一样收集老年代有短暂停顿
  4. Serial 收集器:单线程收集器,效率高

  5. ParNew 收集器:Serial 的多线程版(新生代默认)

  6. Parlllel Scavenge:多线程收集器,可控制吞吐量 <-> 停顿时间

下一篇

安卓面试总结(6)——Java 虚拟机 II

最后

以上就是背后大地为你收集整理的安卓面试总结(5)——Java 虚拟机 I的全部内容,希望文章能够帮你解决安卓面试总结(5)——Java 虚拟机 I所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(53)

评论列表共有 0 条评论

立即
投稿
返回
顶部