2021面试-JVM

2021面试-JVM

JVM内存模型

程序计数器(线程私有)

  • Java方法指向字节码文件的行号指示器
  • Native方法为空
  • 这是虚拟机唯一一个没有OOM的区域

虚拟机栈(线程私有)

  • 是Java方法的执行的内存模型
  • 方法在执行的时候会创建一个栈帧:包含局部变量表,操作数栈,动态链接,方法返回地址。
  • 栈帧随着方法调用被创建,随着方法结束而销毁。

本地方法栈(线程私有)

  • 和虚拟机栈作用类似,区别是:虚拟机栈为Java方法服务,而本地方法栈是为Native方法服务
  • HotSpot VM将本地方法区和虚拟机栈合二为一。

堆 (线程共享)

  • 运行时数据区
  • 创建的对象和数组都是保存在Java堆内存中。
  • 堆也是垃圾收集器进行垃圾回收的最重要区域。
  • 从GC的角度分为:新生代(Eden,From Survivor,To Survivor)和老年代

方法区/永久代(线程共享)

  • 用于存储JVM加载的类信息。常量和静态变量,即时编译后的代码数据。
  • 包含运行时常量池,用于存放各种字面量和符号引用。

MinorGC的过程(复制-清空-互换)

  • MinorGC采用复制算法
  • eden,from复制到to区 , 年龄+1
  • 清空eden,from
  • to和from互换

MajorGC的过程:

  • 首先扫描一次老年代,标记出存活的对象,回收没有标记的对象。
  • 耗时比较长,会产生内存碎片

产生OOM:

  • 永久带,GC不会在主程序运行期间对永久带进行清理,所以随着加载class的增多而胀满,最终抛出OOM
  • 老年代,当有大的对象老年代装不下的是就会抛出OOM

老年代:

  • 存放应用周期长的内存对象

垃圾回收

如何确定对象已死?

  • 引用计数法:会产生循环引用的问题
  • 可达性分析法:
    • 通过GCroots作为起点,向下搜索,当对象没有任何的引用链相连,说明已经死亡。
    • VM栈中的引用 — 方法区的静态引用 ---- JNI中的引用

垃圾收集算法:

  • 标记清楚法 : 效率低,内存碎片多
    • 标记:标记处需要回收的对象
    • 清除:清除被标记的对象所占用的空间
  • 复制算法:
    • 将内存分为均等的两块,每次只使用一块,
    • 当一块满了复制存活的对象到另一块
    • 把已使用的清除掉
  • 标记整理算法:
    • 结合了上面两种算法
    • 标记阶段和标记清除差不多,标记后不清理对象,而是将存活的对象移到另一端
    • 然后清除端边界外的对象。
  • 分代收集法:
    • 新生代采用复制算法
      • 每次只有少量的存活对象,只需付出少量的复制成本就可以完美收集
      • 每次只使用eden和from区,将存活的复制到to区
    • 老年代采用标记整理算法
      • 当对象在Survivor区躲过一次GC后,其年龄+1, 默认下年龄到达15的对象移到老年代

垃圾收集器:

  • Serial
    • 最基本的垃圾收集器,单线程,复制算法
    • Client模式下新生代的垃圾回收器
  • ParNew
    • Serial收集器的多线程版本, 复制算法
    • -XX:ParallelGCThreads参数限定线程数
    • Server模式下新生代默认的垃圾回收器
  • Parallel Scavenge
    • 多线程复制算法、高效
    • 利用高吞吐量高效的利用CPU时间
  • Serial Old
    • 单线程,标记整理算法
    • Client默认老年代垃圾收集器
    • Server模式下:
      • 与新生代的Parallel Scavenge搭配使用
      • 作为老年代CMS的后备垃圾收集方案
  • Parallel Old
    • 多线程, 标记整理算法
  • CMS - Concurrent Mark Sweep
    • 多线程,标记清除算法
    • 垃圾收集的四个阶段
      • 初始标记
      • 并发标记
      • 重复标记
      • 并发清除
    • 美团技术相关文章
  • G1: Garbage first
    • 标记整理算法,不产生内存碎片
    • 非常精准的停顿时间,不牺牲吞吐量的前提下, 实现低停顿垃圾回收

多路复用IO模型

  • 会有一个线程不断的轮询多个socket的状态,只有当socket真正有读写事件的时候,
  • 才真正调用实际的IO读写操作。

相关参数:

堆设置

  • -Xms:初始堆大小
  • -Xmx:最大堆大小
  • -XX:NewSize=n:设置年轻代大小
  • -XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
  • -XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
  • -XX:MaxPermSize=n:设置持久代大小

收集器设置

  • -XX:+UseSerialGC:设置串行收集器
  • -XX:+UseParallelGC:设置并行收集器
  • -XX:+UseParalledlOldGC:设置并行年老代收集器
  • -XX:+UseConcMarkSweepGC:设置并发收集器

垃圾回收统计信息

  • -XX:+PrintGC
  • -XX:+PrintGCDetails
  • -XX:+PrintGCTimeStamps
  • -Xloggc:filename

并行收集器设置

  • -XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
  • -XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
  • -XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)

并发收集器设置

  • -XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
  • -XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。

Linux上排查JVM问题

  • top : cpu,内存,磁盘,负载等
  • jstat :gc和类加载情况
  • jmap : 查看jvm内存占用
  • jstack :对账信息,线程信息

类加载-反射

JVM类加载机制

  • 加载-验证-准备-解析-初始化
    • 加载: 在内存中生成一个代表这个类的对象,作为方法区这个类的数据入口。
    • 验证: 确保class文件的字节流中包含的信息是否符合当前虚拟机的要求
    • 准备: 在方法区中分配变量所使用的内存空间。初始化阶段为默认值
    • 解析: 虚拟机将常量池中的符合引用替换为直接引用的过程
    • 初始化: 真正执行类中定义的Java程序代码
      • 初始化是执行类构造器的过程
        • 启动类加载器: Bootstrap ClassLoader
        • 拓展类加载起: Extension ClassLoader
        • 应用程序类加载器: Application ClassLoader
        • 用户自定义加载器: User ClassLoader

双亲委派:

  • 当一个类收到类加载请求,它首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,
  • 每一层类加载器都是如此。
  • 只有当父类反馈自己无法完成这个请求的时候,子类加载器才会尝试自己去加载。
  • 目的是:保证不同的类加载最终得到的是同一个object对象。

双亲委派的几个好处:

  • 可以避免类的重复加载
  • 保证了安全性

双亲委派 “父子加载器”之间是继承关系吗?

双亲委派模型中,类加载器之间的父子关系一般不会以继承的关系来实现,而都是使用组合的关系来复用父加载器的代码

⭐️ 双亲委派是如何实现的 ?

  • 先检查类是否已经被加载过
  • 若没有加载则调用父类加载器的loadClass()进行加载
  • 若父类加载器为空,则默认使用启动类加载器作为父加载器
  • 若父类加载失败,抛出ClassNotFoundException异常后,在调用自己的findClass()进行加载

如何主动破坏双亲委派机制:

  • 自定义一个类加载器
  • 重写其中的loadClass()方法,使其不进行向上委派即可

双亲委派被破坏的列子

    1. 双亲委派出现之前:JDK1.2之后才引入的,在这之前是没有遵守的
    1. JNDI,JDBC等需要加载SPI接口实现类的情况。
    1. 为了实现热插拔热部署的工具。
    1. Tomcat等容器的出现
    1. OSGI,Jigsaw等模块化技术的应用

Person p = new Person()在内存中发生了什么?

  • 将Person.class文件加载进内存中。
  • 在栈中为p开辟一个变量空间
  • 在堆中为对象分配空间
  • 对对象中的成员变量进行初始化
  • 调用静态代码块,非静态代码块对象进行初始化(没有就不执行)
  • 调用构造方法对对象进行初始化,对象初始化完毕
  • 将对象的内存地址赋值给p变量,让p变量指向该对象。

什么情况下创建的对象不在堆中?

  • jit编译器下的逃逸分析算法会分析类使用情况
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值