- JVM内存模型?堆和栈?
- GC分代算法?
根搜索算法、标记-清除算法、复制算法、标记-整理算法
根搜索算法:设立若干种根对象,当任何一个根对象到某一个对象均不可达时,则认为这个对象是可以被回收的。
可以当做GC roots的对象有以下几种:
(1.JVM stack; 2/3.方法区;4.本地方法栈)
1. 虚拟机栈(栈帧中的本地变量表)中的引用的对象
2. 方法区中的类静态属性引用的对象
3. 方法区中的常量引用的对象
4. 本地方法栈中JNI(Native方法)的引用对象
标记-清除算法:当堆中的有效内存空间(available memory)被耗尽的时候,就会停止整个程序(也被成为stop the world),然后进行两项工作,第一项则是标记,第二项则是清除。
(1)标记:标记的过程其实就是,遍历所有的GC Roots,然后将所有GC Roots可达的对象标记为存活的对象。
(2)清除:清除的过程将遍历堆中所有的对象,将没有标记的对象全部清除掉。
复制算法:复制算法将内存划分为两个区间,在任意时间点,所有动态分配的对象都只能分配在其中一个区间(称为活动区间),而另外一个区间(称为空闲区间)则是空闲的,当有效内存空间耗尽时,JVM将暂停程序运行,开启复制算法GC线程。接下来GC线程会将活动区间内的存活对象,全部复制到空闲区间,且严格按照内存地址依次排列,与此同时,GC线程将更新存活对象的内存引用地址指向新的内存地址。此时,空闲区间已经与活动区间交换,而垃圾对象现在已经全部留在了原来的活动区间,也就是现在的空闲区间。事实上,在活动区间转换为空间区间的同时,垃圾对象已经被一次性全部回收。
标记-整理算法:标记-整理算法与标记-清除算法非常相似,它也是分为两个阶段:标记和整理。
(1)标记:它的第一个阶段与标记/清除算法是一模一样的,均是遍历GC Roots,然后将存活的对象标记。
(2)整理:移动所有存活的对象,且按照内存地址次序依次排列,然后将末端内存地址以后的内存全部回收。因此,第二阶段才称为整理阶段。
标记/整理算法不仅可以弥补标记/清除算法当中,内存区域分散的缺点,也消除了复制算法当中,内存减半的高额代价,标记/整理算法唯一的缺点就是效率也不高,不仅要标记所有存活对象,还要整理所有存活对象的引用地址。
参考:JVM学习之GC常用算法
- GC的过程?
- 垃圾收集器?
七种垃圾收集器简单比较:
年轻代:Serial、ParNew、Parallel Scavenge;
年老代:Serial Old、Parallel Old、CMS(Concurrent Mark Sweep);
G1(Garbage Firest);
1、Serial收集器
新生代单线程的收集器,采用复制算法,在进行收集垃圾时,必须stop the world,
它是虚拟机运行在Client模式下的默认新生代收集器;可以和Serial Old、CMS组合使用;
2、ParNew收集器
是Serial收集器的多线程版本,采用复制算法,回收时需要stop-the-world;
许多运行在Server模式下的虚拟机中首选的新生代收集器;可以和Serial Old、CMS组合使用;
除Serial外,只有它能与CMS收集器配合工作;
3、Parallel Scavenge收集器
新生代收集器,使用复制算法又是并行的多线程收集器,关注系统吞吐量;
CMS等收集器的关注点在于尽可能地缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量(Throughput)。所谓的吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量 = 运行用户代码时间/(运行用户代码时间+垃圾收集时间),虚拟机总共运行了100分钟,其中垃圾收集花费1分钟,那吞吐量就是99%。
停顿时间越短越适合需要与用户交互的程序,良好的响应速度能提升用户体验,而高吞吐量则可以高效率地利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。
参数:
-XX:MaxGCPauseMillis:最大垃圾收集停顿时间;
-XX:GCTimeRatio:吞吐量大小;
-XX:UseAdaptiveSizePolicy:自适应的调节策略;打开参数后就不需要手动指定新生代的大小、Eden与Survivor区的比例、晋升老年代对象年龄等细节参数了,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整相关参数;
4、Serial Old收集器
是Serial收集器的老年代版本,同样是单线程收集器,使用标记整理算法。
5、Parallel Old收集器
是Parallel Scavenge收集器的老年代版本,使用多线程和标记整理算法。
6、CMS收集器(Concurrent Mark Sweep)
老年代收集器;是一种以获得最短回收停顿时间为目标的收集器,基于标记清除算法。
过程如下:初始标记,并发标记,重新标记,并发清除;其中初始标记和重新标记过程需要stop-the-world;初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快;并发标记阶段就是进行GC Roots Tracing的过程;而重新标记阶段则是为了修正并发标记期间用用户程序继续运作而导致标记产生变动的那一部分对象的标记记录。
优点是并发收集,低停顿,缺点是对CPU资源非常敏感,无法处理浮动垃圾,收集结束会产生大量空间碎片。
参数:
UserCMSCompactAtFullCollection:默认开启,FullGC时进行内存碎片整理,整理时用户进程需停止,即发生Stop The World;
CMSFullGCsBeforeCompaction:设置执行多少次不压缩的Full GC后,执行一个带压缩的(默认为0,表示每次进入Full GC时都进行碎片整理);
7、G1收集器
是基于标记整理算法实现的,不会产生空间碎片,
过程:初始标记、并发标记、最终标记、筛选回收
优点:并行与并发、分代收集、空间整合、可预测的停顿;
可以精确地控制停顿,将堆划分为多个大小固定的独立区域,并跟踪这些区域的垃圾堆积程度,在后台维护一个优先列表,每次根据允许的收集时间,优先回收垃圾最多的区域(Garbage First)。
- GC时会对程序有什么影响?频繁GC怎么办?
- JVM判断对象是否可回收?
通过一系列的GC Roots作为起始点,从这些节点开始向下搜索,搜索所走过的路径成为引用链,当一个对象到GC Roots没有任何引用链相连,即从GC Root到这个对象不可达,则证明此对象可以被回收。
哪些对象可以作为GC Roots?
(1.JVM stack;2/3.方法区;4.本地方法栈)
- 类加载的过程?
类从被加载到虚拟机内存中开始,到卸载出内存,整个生命周期包括7个阶段:
加载、验证、准备、解析、初始化、使用和卸载。
其中类加载的过程包括了加载、验证、准备、解析、初始化五个阶段。在这五个阶段中,加载、验证、准备和初始化这四个阶段发生的顺序是确定的,而解析阶段则不一定,它在某些情况下可以在初始化阶段之后开始。
加载
1.字节流;2.方法区数据结构;3.对象-访问入口;
验证
文件格式的验证、元数据的验证、字节码验证和符号引用验证;
准备
为类变量分配内存并设置类变量初始值;这里所设置的初始值通常情况下是数据类型默认的零值(如0、0L、null、false等),而不是被在Java代码中被显式地赋予的值。
解析
解析阶段是虚拟机将常量池中的符号引用转化为直接引用的过程;
初始化类变量和其他资源;
- 类加载器?双亲委派模型?
- 什么是热部署?什么是热加载?
热加载的实现原理主要依赖java的类加载机制,在实现方式可以概括为在容器启动的时候起一条后台线程,定时的检测类文件的时间戳变化,如果类的时间戳变掉了,则将类重新载入。
对比反射机制,反射是在运行时获取类信息,通过动态的调用来改变程序行为;
热加载则是在运行时通过重新加载改变类信息,直接改变程序行为。
热部署原理类似,但它是直接重新加载整个应用,这种方式会释放内存,比热加载更加干净彻底,但同时也更费时间。
- 四种引用类型?应用场景?
强引用、软引用、弱引用、虚引用;
a、强引用(Strong Reference)--不会被回收
程序代码中普遍存在的,比如Object obj = new Object(),只要存在强引用,GC收集器永远不会回收被引用的对象。
b、软引用(Soft Reference) --内存紧张的时候被回收
非必须的对象,是否回收,要看当前内存情况,如果紧张,则进行回收,否则不回收。当程序抛出内存溢出异常时,肯定不存在这种类型的对象。
c、弱引用(Weak Reference) --下一次GC回收,即必被回收;
被弱引用关联的对象只能生存到下一次GC发生之前,即每次必被回收。
d、虚引用(Phantom Reference) --GC时回收,无法通过引用取得对象值;用来检测对象是否已经从内存删除。
幽灵引用或者幻影引用,一个对象是否有虚引用的存在不会影响到其生存时间,无法通过虚引用获取对象实例。为一个对象设置虚引用关联的唯一目的是希望能在这个对象被回收时受到一个系统通知。