优秀的编程知识分享平台

网站首页 > 技术文章 正文

面试「JAVA进阶」JVM(java 面试 jvm)

nanyue 2024-10-28 16:41:59 技术文章 4 ℃

1、内存模型

1.1、堆

  1. 堆是所有线程共享的,主要存放对象实例和数组。
  2. 新生代和老年代的比例是1:2。
  3. 新生代中三个区域的比例是 8 : 1 : 1。

1.1.1、新生代

对象分配在eden区中,当eden区满时会触发minor gc,将eden区中存活的对象,复制到survivor0区中,清空eden区,当survivor0中满了时,会将存活的对象复制到survivor1区中,然后将survivor0和survivor1交换,保持survivor1是空的。每经过一次yong gc 年龄就+1。

  • Eden

对象创建,对象分配在eden区,当eden区满了,再创建对象的时候,会触发minor gc,进行Eden和from surivior区域的垃圾回收。

  • FromServivor
  • ToSurvivor

minor gc后还存活的对象会被放入此区域,当对象年龄到达阈值后会进入老年代。或者to surivior区域满了,会将对象放入老年代。

1.1.2、老年代

  1. 大对象,需要大量连续内存空间的对象
  2. 长期存活的对象,对象年龄超过15(默认值)
  3. yong gc后survivor区容不下的对象。

1.2、JVM栈

线程私有的,每个线程都有一个栈,主要存放当前线程的局部变量,程序运行状态,方法返回值,方法出口等。

1.3、本地方法栈

为虚拟机使用到的native方法服务。

1.4、方法区

用于存放已经被夹在的类信息,常量,静态变量,1.8后取消了永久代,增加了元空间,元空间并不在虚拟机中,而是用的是本地内存。元空间中存放类的元信息,静态变量和常量池移入堆中。

1.5、程序计数器

  1. 程序私有,生命周期与程序相同
  2. 当前线程所执行的字节码的行号指示器。
  3. 用来实现分支,循环,跳转,异常等功能。

2、常量池中包括什么

常量池在编译时期确定,存放在编译生成的class文件中,包含了基本数据类型和对象类型(String和数组)

3、如何判断对象是否存活

使用什么方法标记一个对象可回收?

  1. 引用计数法,每个对象都有一个引用计数器,被引用+1 当引用数为0即为可被GC的对象
  2. 可达性分析:从根节点出发,向下搜索,未访问到的对象标记为不可达,可被回收

4、哪些对象可以用为GC ROOT对象

  1. 虚拟机栈中引用的对象。
  2. 方法区中静态对象引用的对象。
  3. 方法区中常量引用的对象。
  4. 本地方法栈中引用的对象。

5、GC策略

  1. 标记清除法,从根节点进行扫描,对存活的对象进行标记,标记完成后,再扫描整个空间中未被标记的对象,进行清理。容易造成内存碎片
  2. 复制,将内存划分为两份,当其中一份内存满了时,从根节点扫描,将存活的对象复制到另一份内存中。不会出现内存碎片问题,但需要两倍的空间。
  3. 标记整理,如标记清除法一样,标记对象,清除后将所有存活对象向左移。避免了内存碎片和两倍空间的问题,但增加了移动对象的成本。

6、具体GC收集器

  1. 串行垃圾收集器,serial
  2. 并行垃圾收集器 parNew,parallel 注重吞吐量
  3. cms 注重最短回收停顿时间
  4. G1

cms和G1的区别 :

  • cms是新生代的垃圾收集器,采用标记清除。
  • G1是新生代和老年代的垃圾收集器,采用标记整理。
  • cms会产生内存碎片,G1并不会。
  • Cms追求最小停顿时间,G1是达到可控的停顿时间,尽可能提高吞吐量。

7、什么样的对象进入老年代

  1. 大对象,需要大量连续内存空间的对象。
  2. 长期存活的对象,对象年龄超过15(默认值)。
  3. yong Gc后survivor区容不下的对象。

8、为什么要区分新生代和老年代

  1. 对象的生存情况不同使用不同的GC算法。
  2. 新生代对象可能被频繁的创建和回收,老年代回收较少。

9、survivor区存在的意义

  1. 为了提高对象进入老年代的门槛,减少fullGC的次数,因为fullGC很耗时。
  2. 两个survivor的作用是为了减少survivor区的内存碎片。

10、什么是yangGC

对年轻代进行gc。触发条件:

  1. eden区不足清空eden from to中没被引用的对象。将eden from中存活的对象 复制到 to中。将to中的对象晋升到old中,包括两类对象,一个是年龄到达阈值,一个是to中放不下。
  2. full gc也会出发yong gc

11、什么时候触发fullGC

  1. 手动触发的GC。
  2. 老年代的空间不足。
  3. 永久代的空间满了(方法区)。
  4. 统计到yong gc晋升到老年代的平均大小大于老年代剩余的大小(老年代的空间不足)。
  5. jvm自身固定频率的fullGC(默认一小时执行一次)。

12、内存的配置参数

  1. xms xmx 配置堆内存的最小和最大值
  2. xmn 年轻代内存的初始大小
  3. xss jvm栈大小

13、对象分配内存的两种方式

  1. 指针碰撞,如果内存对象是规整的,采用指针碰撞来为对象分配内存,所有使用过的内存在指针的一侧,未使用过的内存在指针的另一侧,分配内存只需要移动指针即可。
  2. 空闲列表,内存不规整,使用过的内存和未使用过的内存交织在一起,维护一个内存使用列表,记录那些内存是可用的,在分配的时候找到一块足够大的空间划分给对象,并更新列表上的内容。

14、如何减少GC的开销

  1. 避免显示的调用System.gc。
  2. 尽量减少临时对象的使用。
  3. 对象不使用时,最好显式的置为null。
  4. 尽量使用StringBuffer而不用String累加字符串。
  5. 能用基本类型就是用基本类型。
  6. 尽量少用静态对象变量。

15、什么是JAVA内存模型(JMM)

用于屏蔽掉各种硬件和操作系统的内存访问差异,以实现让java程序在各个平台下都能达到一致的并发效果。

16、什么时happens-before

  1. 保证了内存的可见性
  2. 制定了四个规则:程序顺序规则:一个线程中的每个操作 happens-before 与后续的所有操作。监视器锁规则:一个监视器解锁 happens-before 于 加锁。volatile变量规则: 写操作 happens-before 读操作。传递性 A happens-before B ,B happens-before C ,那么Ahappens-before C。

17、性能调优工具

17.1、jps

jps主要用来输出jvm中运行的进程状态信息。

-l 输出main类或者jar的权限名。

17.2、jstack

jstack pid > log

可以将线程堆栈转存到文件中。

日志分析可以使用fastthread.io。

17.3、jstat

可以显示出虚拟机进程中的classloader、内存、gc等运行数据。

参数

-class pid 类加载统计。

-gc 垃圾回收统计 ,后面跟两个参数一个是间隔输出时间,一个是总共输出次数。

gc日志可以使用 gceasy.io

17.4、jmap

jmap查看堆内存的使用情况:

jmap pid

17.5、jinfo

查看java程序的运行环境参数:

jinfo pid

18、内存栅栏

通过确保从另一个CPU来看,屏障的两边的所有指令都是正确的程序顺序,而保持程序顺序的外部可见性;其次可以实现内存数据可见性,确保内存数据会同步到CPU缓存子系统。

19、JVM产生的内存溢出及解决办法

  1. java heap space

代码中存在大对象的分配,多次GC后仍找不到分配空间。

解决办法:查看是否有大对象分配尤其是大数组。通过jmap把堆内存的日志dump下来,分析日志,如果解决不了增加堆内存的空间。

  1. permspace metaspace

永久代或元空间溢出。生成大量的代理类或者使用自定义的类加载器。

解决办法:查看有没有配置永久代或者元空间的大小。是否长时间没有重启jvm,是否有大量的反射操作。


Tags:

最近发表
标签列表