一 内存图
1.1 内存模型有哪些?有什么区别?
JVM分为:运行时方法区、类加载器、本地接口、执行引擎。
类加载器:根据全类名将class文件加载到运行时方法区的方法区。
执行引擎:执行classes指令。
运行时方法区:执行指令内存。
本地接口:与其他编程语言交互接口。
运行时方法区分为:虚拟机栈、堆、方法区、本地方法栈。
虚拟机栈:用于加载方法,局部变量、请求参数。(单线程有效)
堆:用于存储对象。多个线程有效。
本地方法栈:执行本地方法的区域。
方法区:类信息、常量、静态变量、即时编译的代码(多线程有效)。
程序计数器:很小内存,字节码执行标识器。(当前线程有效)
1.2 堆内存模型在1.7和1.8之间变化?为什么这样变?
1.7:年轻代、老年代、永久代。
1.8:年轻代、老年代、元空间(物理内存)。
因为JRockit没有永久代,HotSpot jvm为了融合JRockit而移除永久代。
1.3 为什么要分代?新生代还要分Eden、From、To区域呢?
根据对象的寿命将对象放在不同的区。对于不同的区采用不同的垃圾回收算法。寿命短的回收频率高点,寿命长的回收频率低点。提高效率。
因为年轻代对象寿命很短,进行GC时候,只有少部分对象存活,将存活的对象复制到TO和老年代。能提高效率和减少内存碎片化。
1.4 JVM什么情况会触发垃圾回收(young gc MinorGC Full GC)?
young gc 触发频率:Eden区被耗尽会被触发。
mixed gc 当老年代大小占用堆阙值,就会触发mixed gc。回收全部年轻代和部分老年代。
Full GC: System.gc()方法被调用、老年代空间不足、方法区空间不足。
1.5 为什么新生代使用标记复制,老年代使用标记压缩法?
年轻代为什么要用标记复制算法:因为年轻代存放对象生命周期较短,垃圾对象多,要复制有用的对象少。执行效率高。
老年代使用标记压缩法:因为老年代的存活对象比较多,移动的元素相对少,还解决了内存碎片化问题。
1.6 内存泄漏和内存溢出是什么?
内存泄漏:对象申请内存,用完后不释放。
内存溢出:创建对象,没有足够内存创建对象。
1.7 详细描述下年轻代进行回收的过程是什么?
在开始GC时候,对象只存在Eden区和from区。紧接着GC时候,eden区对象复制到to区,from区对象根据年龄复制到to和老年代,当from和eden完全为null。to和from交换角色。如果在复制过程中时候to满了,全部对象到老年代。
1.8 对象一定在堆里?常量和变量在哪?
对象创建存在堆中,但是静态对象、常量对象也会存在方法区中。
常量存在方法区中。局部变量存储在栈中,成员变量存储在堆中。
1.9 哪些对象可以作为gc roots?
- 静态对象。
- 常量引用对象。
- 栈帧中引用的对象。
- 被同步锁持有对象。
- 基本数据类型对应对象、异常对象。
1.10 Eden和Survivor的比例分配等?
在年轻代中Eden占有八份,Survivor的From占有一份,to占有一份。
1.11 JVM的空间分配担保策略描述。
JVM采用分代算法,将堆划分为老年代和年轻代。不同的内存采用不用的回收算法。空间担保指的是老年代进行空间分配和担保。
在发生Minor GC前,先检查老年代最大连续可用空间是否大于年轻代所有的对象。如果大于则证明此次Minor GC安全。如果小于且开启了空间分配等保,则查看历次变为老年代对象的平均值,如果大于平均值则尝试进行一次Minor GC,如果小于或者没有设置空间等保进行有一次full gc。
1.12 JVM常用调优参数有哪些?
-Xms初始堆大小
-Xmx最大堆大小
-XX:NewRatio=n 设置年轻代和老年代比值。
-XX:NewSize=n:设置年轻代大小
-XX:+UseSerialGC:设置收集器
1.13 JVM 运行时数据区 方法递归调用存在什么问题?
如果递归调用比较深参数比较多,有可能导致栈溢出。
1.14 跨代引用怎么解决的?
每个Region初始化都会初始化一个RSet,用来记录其他Region指向该Region中对象引用。
1.15 Java线程模型和JVM线程模型区别?
1.16 标记清除多次后老年代产生内存碎片,引起 full gc,接下来可能发生什么问题?
可能对碎片化内存进行压缩。如果设置设置压缩内存次数低,可能导致full gc频率过高。
1.17 高吞吐量该jvm如何调优?
将垃圾回收器设置为G1。高吞吐量尽量避免full gc,可以将eden区设置大点,减少young gc次数,使对象岁数增长慢点,尽可能将对象在young gc解决掉。从而不进入老年代。
1.18 哪些地方会OOM,元空间如何OOM?
集合中有对象的引用,使用未清空,GC不能进行回收。
代码循环中产生过多重复对象。
一次性从数据库取出数据过大。
堆内存值小。
一次加载过多的class文件会导致OOM(内存溢出)。
1.19 永久代和元空间会发生垃圾回收?
当永久代或者元空间满了会出现Full GC。元空间默认是物理机内存的1/64。最大内存是物理机内存的1/4或者1G.
二 回收算法
2.1 jvm垃圾回收算法有哪些?之间区别?
标记清除算法、标记压缩算法、标记复制算法、分代算法
标记清除:需要遍历两遍对象,效率低,而且产生碎片化。
标记压缩算法:也是需要遍历两遍对象,而且需要对对象移动。解决内存碎片化,但是效率也比较低。
标记复制算法:清理的对象多的时候效率高,且无碎片化。Survivor分两个区,每次只能使用一个。
分代算法:根据回收对象特点选择,年轻代复制算法、老年代标记清除和标记压缩。
2.2 调用System.gc()是否会立即回收,为什么?
System.gc()只是告诉虚拟机要进行回收,什么时候回收是由虚拟机说的算。垃圾回收机制是内存快不够用了才回收。
2.3 有没有出现栈溢出?
栈内存溢出,一般栈内存局部变量过多,导致内存溢出。常见:出现递归方法,参数过多,递归过深、递归没有出口。
三 回收器
3.1 垃圾回收器有哪些?分别运用了什么垃圾回收算法?
串行收集器、并行收集器、cms收集器、G1收集器、ZGC收集器
CMS垃圾回收器:针对老年代进行垃圾回收,采用标记清除算法。
G1垃圾回收器:取消物理上的老年代和年轻代的划分。而是将堆划分若干个区域,每个区域包含逻辑上的年轻代和老年代。采用年轻代收集、混合收集、full gc。
3.2 CMS垃圾回收器怎么解决标记清除导致的碎片问题?
设置多少次Full GC触发一次压缩,默认值为0,代表每次进入Full GC都会触发压缩。
四 类加载器
4.1 什么是类加载机制?在哪个区域进行?
虚拟机将类的class文件加载到内存中,并对数据校验、准备、解析、初始化,最终形成虚拟机直接使用的Java类型,叫做类加载机制。
类加载过程放到虚拟机外部执行。
4.2 什么是双亲委派机制?有什么用?
类加载器有:启动加载器、扩展加载器、应用加载器。
如果一个类收到加载请求,先不进行加载先给父类进行加载。依次递进,最上层加载器没有父类加载器。如果父类加载器反馈无法加载这个类,子类才去进行加载。
保证Java程序的稳定运作。如果一个类书写为java.lang.Object 。系统中出现两个不用的 Object 类,Java体系中最基本的行为无法保证。
4.3 JVM字节码文件对象的结构?
版本信息、生成时间;
类中涉及常量池;
类的构造器;
main方法信息;
4.4 一个对象new具体过程,如果发生OOM,JVM会做什么?
虚拟机接到new指令时,先检查常量池是否加载对应的类。如果没有,先加载对象的类。类加载之后接下来时分配内存。若堆内存是绝对工整的,使用“指针碰撞”分配内存,如果不规整就从“空闲列表”分配。划内存时候还需要考虑一个问题并发,两种方式:CAS同步处理或本地线程分配缓冲。然后内存初始化操作,执行init方法。
内存分配两种方式
保证线程安全两种方式:
4.5 类加载器加载文件过程?怎么扫描的方法和属性,放在了哪?
整个生命周期经历7段,加载、验证、准备、解析、初始化、使用、卸载。
4.6 JVM的编译优化常见什么?
为了使代码运行效率更高,虚拟机的编译器做着优化工作。常见如下:方法内联 逃逸分析 公共子表达式消除 数组边界检查消除 。
4.7 什么是逃逸分析?
目前Java虚拟机中比较前沿的优化技术,它并不是直接优化代码的手段,而是为其 他优化措施提供依据的分析技术。
分析对象动态作用域,当一个对象在方法里面被定义后,它可能被外部方法所引用,例如作为调用参数传递 到其他方法中,这种称为方法逃逸; 甚至还有可能被外部线程访问到,譬如赋值给可以在其他线程中访问的实例变量,这种称为线程逃逸; 从不逃逸、方法逃逸到线程逃逸,称为对象由低到高的不同逃逸程度。