194.说一下 jvm 的主要组成部分?及其作用?
JVM的基本结构及其各部分详解(一)
JVM的基本结构及其各部分详解(二)
195.说一下 jvm 运行时数据区?
JVM入门——运行时数据区
196.说一下堆栈的区别?
堆是进程资源,栈是线程资源,
1.栈内存存储的是局部变量而堆内存存储的是实体;
2.栈内存的更新速度要快于堆内存,因为局部变量的生命周期很短;
3.栈内存存放的变量生命周期一旦结束就会被释放,而堆内存存放的实体会被垃圾回收机制不定时的回收。
197.队列和栈是什么?有什么区别?
1、队列先进先出,栈先进后出。
2、对插入和删除操作的"限定"不同。
栈是限定只能在表的一端进行插入和删除操作的线性表。
队列是限定只能在表的一端进行插入和在另一端进行删除操作的线性表。
3、遍历数据速度不同。
栈只能从头部取数据,也就最先放入的需要遍历整个栈最后才能取出来,而且在遍历数据的时候还得为数据开辟临时空间,保持数据在遍历前的一致性。
队列则不同,它基于地址指针进行遍历,而且可以从头或尾部开始遍历,但不能同时遍历,无需开辟临时空间,因为在遍历的过程中不影像数据结构,速度要快的多
198.什么是双亲委派模型?
双亲委派模型工作过程是:如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。每个类加载器都是如此,只有当父加载器在自己的搜索范围内找不到指定的类时(即ClassNotFoundException
),子加载器才会尝试自己去加载。
Java自定义类加载器与双亲委派模型
Tomcat 类加载器之为何违背双亲委派模型
199.说一下类加载的执行过程?
类从被加载到JVM中开始,到卸载为止,整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载七个阶段。 其中类加载过程包括加载、验证、准备、解析和初始化五个阶段。
Java类加载过程
200.怎么判断对象是否可以被回收?
(1)引用计数法
(2)可达性算法
Java虚拟机如何判断对象可以被回收
201.java 中都有哪些引用类型?
强引用、软引用、弱引用、虚引用
java中四种引用类型
202.说一下 jvm 有哪些垃圾回收算法?
复制算法、标记清除算法、标记整理算法、分带收集算法
分代收集法是目前大部分JVM所采用的方法,其核心思想是根据对象存活的不同生命周期将内存划分为不同的域,一般情况下将GC堆划分为老生代(Tenured/Old Generation)和新生代(Young Generation)。老生代的特点是每次垃圾回收时只有少量对象需要被回收,新生代的特点是每次垃圾回收时都有大量垃圾需要被回收,因此可以根据不同区域选择不同的算法。
目前大部分JVM的GC对于新生代都采取Copying算法,因为新生代中每次垃圾回收都要回收大部分对象,即要复制的操作比较少,但通常并不是按照1:1来划分新生代。一般将新生代划分为一块较大的Eden空间和两个较小的Survivor空间(From Space, To Space),每次使用Eden空间和其中的一块Survivor空间,当进行回收时,将该两块空间中还存活的对象复制到另一块Survivor空间中。
而老生代因为每次只回收少量对象,因而采用Mark-Compact算法。
另外,不要忘记在Java基础:Java虚拟机(JVM)中提到过的处于方法区的永生代(Permanet Generation)。它用来存储class类,常量,方法描述等。对永生代的回收主要包括废弃常量和无用的类。
对象的内存分配主要在新生代的Eden Space和Survivor Space的From Space(Survivor目前存放对象的那一块),少数情况会直接分配到老生代。当新生代的Eden Space和From Space空间不足时就会发生一次GC,进行GC后,Eden Space和From Space区的存活对象会被挪到To Space,然后将Eden Space和From Space进行清理。如果To Space无法足够存储某个对象,则将这个对象存储到老生代。在进行GC后,使用的便是Eden Space和To Space了,如此反复循环。当对象在Survivor区躲过一次GC后,其年龄就会+1。默认情况下年龄到达15的对象会被移到老生代中。
目前比较主流的垃圾收集算法有四种:标记-清除算法、复制算法、标记-整理算法、分代收集算法。具体分析对比如下:
分类 | 标记-清除算法(Mark-Sweep) | 复制算法(Coping) | 标志-整理算法(Mark-Compact) | 分代收集算法(Generational Collection) |
---|---|---|---|---|
进行整理 | 否 | 是 | 是 | 是 |
算法实现过程 | 该算法分为两个过程:标记和清除。先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的要回收的对象。 | 将内存按容量划分为大小相等的两块区域,每次使用其中的一块,当一块的内存用完了,执行GC算法时将还存活的对象整理复制到另外一块上,然后清理所有的内存块。 | 该算法分为两个过程:标记和整理。首先标记出所有需要回收的对象,然后让存活的对象都向内存的一端移动,然后直接清除掉端边界以外的内存。 | 根据对象存活周期的不同将内存划分为几块,一般是划分为新生代和老年代,然后根据各个年代的特点采用不同的最适当的收集算法。 |
优点 | 简单,易于实现 | 内存分配时算法不产生内存碎片 | 内存分配时算法不产生内存碎片,也比较易于实现 | 分代收集,效率较高 |
缺点 | 1、效率低 2、会产生大量不连续的内存碎片 | 空间消耗太大,内存被压缩为原来的一半 | 算法复杂度大,执行步骤较多 | 算法复杂度大,执行步骤较多 |
203.说一下 jvm 有哪些垃圾回收器?
jdk1.7 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)
jdk1.8 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)
jdk1.9 默认垃圾收集器G1
-XX:+PrintCommandLineFlagsjvm参数可查看默认设置收集器类型
-XX:+PrintGCDetails亦可通过打印的GC日志的新生代、老年代名称判断
3.1. Serial/Serial Old
最古老的收集器,是一个单线程收集器,用它进行垃圾回收时,必须暂停所有用户线程。Serial是针对新生代的收集器,采用Copying算法;而Serial Old是针对老生代的收集器,采用Mark-Compact算法。优点是简单高效,缺点是需要暂停用户线程。
3.2. ParNew
Seral/Serial Old的多线程版本,使用多个线程进行垃圾收集。
3.3. Parallel Scavenge
新生代的并行收集器,回收期间不需要暂停其他线程,采用Copying算法。该收集器与前两个收集器不同,主要为了达到一个可控的吞吐量。
3.4. Parallel Old
Parallel Scavenge的老生代版本,采用Mark-Compact算法和多线程。
3.5. CMS
Current Mark Sweep收集器是一种以最小回收时间停顿为目标的并发回收器,因而采用Mark-Sweep算法。
3.6. G1
G1(Garbage First)收集器技术的前沿成果,是面向服务端的收集器,能充分利用CPU和多核环境。是一款并行与并发收集器,它能够建立可预测的停顿时间模型。
204.详细介绍一下 CMS 垃圾回收器?
cms只会回收老年代和永久带(1.8开始为元数据区,需要设置CMSClassUnloadingEnabled),不会收集年轻带;
cms是一种预处理垃圾回收器,它不能等到old内存用尽时回收,需要在内存用尽前,完成回收操作,否则会导致并发回收失败;所以cms垃圾回收器开始执行回收操作,有一个触发阈值,默认是老年代或永久带达到92%;
CMS 处理过程有七个步骤:
1. 初始标记(CMS-initial-mark) ,会导致swt;
2. 并发标记(CMS-concurrent-mark),与用户线程同时运行;
3. 预清理(CMS-concurrent-preclean),与用户线程同时运行;
4. 可被终止的预清理(CMS-concurrent-abortable-preclean) 与用户线程同时运行;
5. 重新标记(CMS-remark) ,会导致swt;
6. 并发清除(CMS-concurrent-sweep),与用户线程同时运行;
7. 并发重置状态等待下次CMS的触发(CMS-concurrent-reset),与用户线程同时运行;
cms运行流程图如下所示:
205.新生代垃圾回收器和老生代垃圾回收器都有哪些?有什么区别?
新生代垃圾收集器
-
- Serial
Serial收集器是单线程的一个收集器,但它的单线程的意义是它只会使用一个CPU或者一条收集线程去完成垃圾收集工作,更重要的是在它进行垃圾收集的时候,必须暂停其他所有的工作线程,直到它收集结束。
分代收集算法:新生代单线程采用复制算法,并暂停所有用户线程;老年代单线程采用标记-整理算法,并暂停所有用户线程。 - ParNew
ParNew收集器是Serial收集器的多线程版。其基本操作和Serial算法基本一致。该收集器一般搭配CMS收集器进行工作。‘
分代收集算法:新生代采用复制算法,并暂停所有用户线程;老年代采用标记-整理算法,并暂停所有用户线程。 - Parallel Scavenge
Parallel Scavenge收集器是也与ParNew算法十分相似,但是与其他收集器的关注点大多是尽可能缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器目的是达到一个可控制的吞吐量。吞吐量就是CPU用于运行用户代码的时间与CPU总消耗的时间的比值。即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间),举个例子,虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%。
GC自适应调节策略:JVM会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大吞吐量。Parallel Scavenge收集器可以搭配自适应调节策略。
分代收集算法:新生代采用复制算法,并暂停所有用户线程;老年代采用标记-整理算法,并暂停所有用户线程。
- Serial
老年代垃圾收集器
-
- Serial Old
Serial Old是Serial算法的老年代版本,同样是一个单线程收集器。该收集器主要是给Client模式下的虚拟机使用的。
分代收集算法:新生代单线程采用复制算法,并暂停所有用户线程;老年代单线程采用标记-整理算法,并暂停所有用户线程。 - Parallnel Old
Parallnel Old是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。 - CMS
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。该收集器是基于”标记-清除“算法实现的。
CMS收集器的收集过程分为以下4个步骤:
1、初始标记(Stop the World,标记GC Roots能直接关联到的对象)
2、并发标记(进行GC Roots Tracing的过程)
3、重新标记(Stop the World,休整并发标记期间因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录)
4、并发清除(并发清除无用的对象)
缺点:
a、CMS收集器对CPU资源非常敏感,并发阶段占用的线程资源较多。
b、CMS收集器无法处理浮动垃圾。因为CMS并发清理阶段用户线程还在运行着,所以也会有相应的垃圾产生,这部分垃圾CMS无法在此次的收集中处理掉它们。
c、CMS收集器由于是基于“标记-清除”算法,故会产生较多的内存空间碎片。
- Serial Old
新生代和老年代垃圾收集器
-
- G1
G1(Garbage-First)收集器所具备的特点:
1、并行和并发:使用多个CPU来缩短Stop-The-World的时间,部分垃圾收集器原本需要停顿Java线程执行的GC动作,G1收集器仍然可以通过并发的方式让Java程序继续执行。
2、分代收集
3、空间整合:标记-整理算法。
4、可预测的停顿。追求低停顿,并建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒,达到了实时Java的垃圾收集器。
G1收集器分代策略:
G1收集器将整个Java堆划分为多个大小相等的独立区域(Region)。G1收集器之所以可以有计划地避免在整个Java堆中进行全区域的垃圾收据,是因为G1收集器跟踪各个Region里面的垃圾堆积的价值大小(回收获得的空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region。即Grabage-First。
在G1收集器中,Region之间的对象引用以及其他收集器中的新生代与老年代之间的对象引用,虚拟机都是通过
Remembered Set来避免全堆扫描的。G1中每个Region都有一个与之对应的Remembered Set。在新建对象时,JVM会将相关的引用信息记录到被引用对象所属的Region的Remembered Set中。当进行回收时,在GC根节点的枚举范围中加入Remembered Set即可保证不对堆进行扫描也不会有遗漏。
G1收集器的手机阶段也分以下几个步骤:
1、初始标记(只是标记一下GC Roots能直接关联到的对象,并修改可以得Region中创建新对象,这阶段需要停顿线程,但耗时很短)
2、并发标记(从GC Roots开始对堆中对象进行可达性分析,找出存活对象)
3、最终标记(修正在并发标记期间因月洪湖程序继续运行而导致标记产生变动的那一部分标记记录)
4、筛选回收(首先对各个Regin的回收价值和成本进行排序,根据用户所期待的GC停顿时间指定回收计划,回收一部分Region)
- G1
最后,我们总结一下JVM中的垃圾收集器:
分类 | 所属分代 | 使用线程 | 使用算法 |
---|---|---|---|
Serial | 新生代 | 单线程 | 复制(新)、标记-整理(老) |
ParNew | 新生代 | 多线程 | 复制(新)、标记-整理(老) |
Parallel Scavenge | 新生代 | 多线程 | 吞吐量优先算法 |
Serial Old | 老生代 | 单线程 | 复制(新)、标记-整理(老) |
Parallel Old | 老生代 | 多线程 | 复制(新)、标记-整理(老) |
CMS | 老生代 | 多线程 | 标记-清除算法(初始标记、并发标记、重新标记、并发清除) |
G1 | 新生代&&老生代 | 多线程 | 标记-整理算法(初始标记、并发标记、最终标记、筛选回收) |
206.简述分代垃圾回收器是怎么工作的?
虚拟机中的共划分为三个代:年轻代(Young Generation)、年老点(Old Generation)和持久代(Permanent Generation)。其中持久代主要存放的是Java类的类信息,与垃圾收集要收集的Java对象关系不大。年轻代和年老代的划分是对垃圾收集影响比较大的。
年轻代:
所有新生成的对象首先都是放在年轻代的。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。年轻代分三个区。一个Eden区,两个 Survivor区(一般而言)。大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当这个 Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当这个Survivor去也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制“年老区(Tenured)”。需要注意,Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时存在从Eden复制过来对象,和从前一个Survivor复制过来的对象,而复制到年老区的只有从第一个Survivor去过来的对象。而且,Survivor区总有一个是空的。同时,根据程序需要,Survivor区是可以配置为多个的(多于两个),这样可以增加对象在年轻代中的存在时间,减少被放到年老代的可能。
年老代:
在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。
持久代:
用于存放静态文件,如今Java类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate 等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。持久代大小通过-XX:MaxPermSize=<N>进行设置。
jps(Java Virtual Machine Process Status Tool)
jps主要用来输出JVM中运行的进程状态信息
jstack
jstack主要用来查看某个Java进程内的线程堆栈信息
jmap(Memory Map)和jhat(Java Heap Analysis Tool)
jmap用来查看堆内存使用状况,一般结合jhat使用。
jstat(JVM统计监测工具)
208.常用的 jvm 调优的参数都有哪些?