微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

2021面试-JVM

2021面试-JVM

文章目录

JVM内存模型

程序计数器(线程私有)

虚拟机栈(线程私有)

本地方法栈(线程私有)

  • 和虚拟机栈作用类似,区别是:虚拟机栈为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数。并行收集线程数。

类加载-反射

JVM类加载机制

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

双亲委派:

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

双亲委派的几个好处:

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

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

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

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

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

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

双亲委派被破坏的列子

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

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

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

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

  • jit编译器下的逃逸分析算法会分析类使用情况

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐