面试 虚拟机篇

JVM 内存结构
  • 线程私有
    • 程序计数器 【记录当前线程执行到代码的位置】
    • 虚拟机栈 【java 方法内的局部变量,方法参数;区别于本地方法,本地方法用的是本地方法栈】
  • 线程共享
    • 堆 【存储对象的信息】
    • 方法区 【存储类的信息】

在这里插入图片描述

  • 解释器:将 java 字节码翻译成适用于各个平台的机器码,以便于 CPU 执行 【当遇到相同代码时,会重复解释,效率不是很高】
  • 即时编译器:负责发现热点代码,并把热点代码翻译成机器码缓存起来,下一次再遇到这种代码时,解释器就不会再去解释了,直接从即时编译器中缓存的机器码中取出 【可以提高效率】

哪些部分会出现内存溢出
  • 不会出现内存溢出的区域 - 程序计数器
  • 出现 OutOfMemoryError 的情况
    • 堆内存耗尽 – 对象越来越多,又一直在使用,不能被垃圾回收
    • 方法区内存耗尽 – 加载的类越来越多,很多框架都会在运行期间动态产生新的类
    • 虚拟机栈累计 – 每个线程最多会占用 1M 内存,线程个数越来越多,而又长时间运行不销毁时
  • 出现 StackOverflowEroor 的区域
    • 虚拟机栈内部 – 方法调用次数过多
  • 内存泄露:当某些对象不再被应用程序所使用,但是由于仍然被引用而导致垃圾收集器不能回收

方法区与永久代,元空间之间的关系
  • 方法区是 JVM 规范中定义的的一块内存区域,用来存储元数据,方法字节码,即时编译器需要的信息等 (仅仅是个定义)
  • 永久代是 HotSpot 虚拟机对JVM 规范的实现 (1.8 之前) (方法区的实现)
  • 元空间时 HotSpot 虚拟机对JVM 规范的实现 (1.8 以后),使用本地内存作为这些信息的存储空间 (方法区的实现)

JVM 内存参数

对于 JVM 内存配置参数:-Xmx10240m,-Xms10240m,-Xmn5120m,-XX:SurivorRatio=3,其最小内存值和Survivor去总大小分别是

  • -Xmx:最大内存数
  • -Xms:最小内存数
  • -Xmn:虚拟机中新生代的内存数
  • -XX:SurivorRatio:新生代伊甸园区与幸存区中的from区的比例,默认是8:1
  • -XX:NewRatio=2:1 -> 新生代:老年代 = 1:2
  • -Xss:控制每个线程占用内存的大小

JVM 垃圾回收算法
  • 标记清除 (几乎已经被淘汰掉了)

在这里插入图片描述

  • GC Root:根对象,简单来说就是那些一定不能被清除的对象,比如正在使用的对象

  • 加了标记的对象保存下来,没有加标记的对象就清除

  • 虽然释放了很多内存,但是造成了内存碎片的问题,这些内存碎片的利用率不高

  • 标记整理 (常用于老年代的垃圾回收)

在这里插入图片描述

  • 清除掉垃圾之后,会对内存进行一次整理,解决了内存碎片的问题

  • 随之而来的问题是效率会降低

  • 标记复制 (常用于新生代的垃圾回收,但是不适用于老年代的垃圾回收,因为老年代存活的对象较多,复制的工作量大)

在这里插入图片描述

  • 将标记出来的对象移动到空闲区
  • 效率比标记整理法更高,但是更耗内存

说说 GC 和 分代回收算法
  • GC 的目的在于实现无用对象内存自动释放,减少内存碎片,加快分配速度
  • GC 要点
    • 回收区域是堆内存,不包括虚拟机栈,在方法调用结束会自动释放方法占用的内存
    • 判断无用对象,使用可达性分析算法,三色标记法标记存活对象,回收未标记对象
    • GC 具体的实现为垃圾回收器
    • GC 大都采用了分代回收思想,理论依据是大部分对象朝生夕灭,用完立刻就可以回收,另有少部分对象会长时间存活,每次很难回收,根据这两类对象的特性将回收区域分为新生代老年代,不同区域应用不同的回收策略
    • 根据 GC 的规模可以分成Minor GC,Mixed GC,Full GC

分代回收与 GC 规模
  • 分代回收
    • 伊甸园 eden,最初对象都分配在这里,与幸存者区称为新生代
    • 幸存者 survivor,当伊甸园内存不足,回收后的幸存对象到这里,分成 from 和 to,采用标记复制算法 (from 和 to 会交换位置,谁空谁是 to)
    • 老年代 old,当幸存者区对象熬过几次回收 (最多15次),晋升到老年代 (幸存区内存不足或大对象会导致提前晋升)
  • GC 规模
    • Minor GC:发生在新生代的垃圾回收,暂停时间短
    • Mixed GC:新生代 + 老年代部分区域的垃圾回收,G1 收集器特有
    • Full GC:新生代 + 老年代完整垃圾回收,暂停时间长,应尽力避免

三色标记与并发漏标问题
  • 用三种颜色记录对象的标记状态
    • 黑色 – 已标记
    • 灰色 – 标记中
    • 白色 – 还未标记
  • 漏标问题 – 记录标记过程中的变化
    • Incremental Update (增量更新)
      • 只要赋值发生变化,被复制的对象就会被记录
    • Snapshot At The Begining,SATB (原始快照)
      • 新加对象会被记录
      • 被删除引用关系的对象也会被记录

垃圾回收器
  • Parallel GC
    • eden 内存不足发生 Minor GC,标记复制 STW
    • old 内存不足发生 Full GC,标记整理 STW
    • 注重吞吐量
  • ConcurrentMarkSweep GC
    • old 并发标记,重新标记时需要 STW,并发清除 (意味着有内存碎片)
    • Failback Full GC
    • 注重响应时间
  • G1 GC (从 JDK9 开始,作为默认的垃圾回收器)
    • 响应时间和吞吐量兼顾
    • 划分成多个区域,每个区域都可以充当 eden,survivor,old,humongous (humongous主要用于存储大对象的,因为G1主要是标记复制算法,大对象的复制比较耗时间)
    • 新生代回收:eden 内存不足,标记复制 STW
    • 并发标记:old 并发标记,重新标记时需要 STW
    • 混合收集:并发标记完成,开始混合收集,参与复制的有 eden,survivor,old,其中 old 会根据暂停时间目标,选择部分回收价值高的区域,复制时 STW
    • Failback Full GC

项目中什么情况会内存溢出,怎么解决的
  • 误用线程池导致的内存溢出 (比如等待队列中任务数过多,或者线程数过多)
  • 查询数据量太大导致的内存溢出
  • 动态生成类导致的内存溢出

类加载过程,双亲委派
  • 类加载过程分为三个阶段

    1 加载

    • 将类的字节码载入方法区,并创建类.class 对象
    • 如果此类的父类没有加载,先加载父类
    • 加载是懒惰执行 (用到类的时候,才会加载)

    2 链接

    • 验证 – 验证类是否符合 Class 规范,合法性,安全性检查
    • 准备 – 为 static 变量分配空间,设置默认值
    • 解析 – 将常量池的符号引用解析为直接引用

    3 初始化

    • 执行静态代码块与非 final 静态变量的赋值 (final 修饰的非引用型静态变量在编译阶段就已经确定好值了,在准备阶段赋值)
    • 初始化是懒惰执行
1. 访问 final static 修饰的基本类型变量,不会触发类的加载
2. 访问 final static 修饰的引用类型变量,会触发类的加载
  • 何为双亲委派模式

    • 所谓的双亲委派,就是指优先委派上级类加载器进行加载,如果上级类加载器
      • 能找到这个类,由上级加载,加载后该类也对下级加载器可见
      • 找不到这个类,则下级类加载器才有资格执行加载 (下级对上级是不可见的)

在这里插入图片描述

  • 双亲委派的目的有两点
    • 让上级类加载器中的类对下级共享 (反之不行),即能让你的类能依赖到 JDK 提供的核心类
    • 让类的加载有优先次序,保证核心类优先加载

对象引用类型分为哪几类?
  • 强引用
    • 普通变量赋值即为强引用,如 A a = new A();
    • 通过 GC Root 的引用链,如果强引用不到该对象,该对象才能被回收
  • 软引用
    • 例如:SoftReference a = new SoftReference(new A);
    • 如果仅有软引用该对象时,首次垃圾回收不会回收该对象,如果内存仍然不足,再次回收时才会释放
    • 软引用自身需要配合引用队列来释放
    • 典型例子是反射数据
  • 弱引用
    • 例如:WearReference a = new WeakReference(new A());
    • 如果仅有弱引用该对象时,只要发生垃圾回收,就会释放对象
    • 弱引用自身需要配合引用队列来释放
    • 典型例子是 ThreadLocalMap 中的 Entry 对象
  • 虚引用
    • 例如:PhantomReference a = new PhantomReference(new A());
    • 必须配合引用队列一起使用,当虚引用引用的对象被回收时,会将虚引用对象入队,由 Reference Handler 线程释放其关联的外部资源
    • 典型例子是 Cleaner 释放 DirectByteBuffer 占用的直接内存

在这里插入图片描述


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值