layout | title | category | tags | keywords | description |
---|---|---|---|---|---|
post |
2 初学JVM之问答式记住垃圾收集器 |
技术 |
VM |
-
怎么判定对象是否存活
判断对象是否存活,有以下几种判断方式:
-
引用计数算法
特点:实现简单,判断效率高
应用方面:python、Squirrel、COM技术和flash palyer技术
缺点:很难解决对象间循环引用问题。 -
可达性分析算法
应用方面:java、C#、Lisp等
基本思路:以成为"GC Root"的对象作为起始点,向下搜索,所走过的路径称为引用链。而如果对象到"GC Root"不可达,就认为该对象不可用。可作为GC Roots的对象:虚拟机栈(栈帧中的本地变量表)中引用的对象、方法区中类静态属性引用的对象、方法区中常量引用的对象和本地方法栈中JNI引用的对象等四种
另外:即使是可达性分析算法中的不可达对象,要被宣告死亡也至少需要两次标记过程。
-
-
java中有哪些引用,分别解释下?
在JDK1.2之后,java将引用扩充为四种:强引用、软引用、弱引用、虚引用。
-
强引用
类似使用
User user = new User()
方式的引用。只要强引用在,GC永远不会回收掉被引用的对象。 -
软引用
用来描述一些还有用但并非必需的对象。在系统将要发生内存溢出异常之前,这些对象会被二次回收。JDK1.2后的使用
SoftReference
实现软引用。 -
弱引用
描述非必需对象,比软引用更弱。被弱引用关联的对象只能生存到下次GC前。使用
WeakReference
实现弱引用。 -
虚引用(幽灵引用、幻影引用)“
为对象设置虚引用的唯一目的就是在对象被收集器收集时受到可以收到系统通知。JDK1.2之后提供了
PhantomReference
实现虚引用。
-
-
垃圾收集器会不会回收方法区,为什么?
在JVM规范中不要求虚拟机堆方法区实现垃圾收集,而且性价比比较低(回收率低)。但方法区(HotSpot中的永久代)也可以进行垃圾回收,主要回收废弃常量和无用的类。
在没有任何对象引用常量池中常量时,如果发生了回收,而且有必要的话,该常量就作为废弃常量清理出常量池。判断常量是否废弃比较简单,但判断类为无用类需要满足以下三种条件:
-
该类所有的实例都被回收。
-
加载该类的ClassLoader也被回收。
-
该类对应的java.lang.Class对象没有在任何地方引用,无法在任何地方通过反射访问该类的方法。
满足条件不一定回收,可以使用
-Xnoclassgc
参数控制,也可以通过-verbose:class
、-XX:+TranceClassLoading
、-XX:+TranceClassUnLoading
查看类加载和卸载信息。 -
-
java中有哪些垃圾回收算法?
-
标记-清除算法
分
标记
和清除
两阶段。先标记要回收的对象,然后在标记完后统一回收被标记的对象。定位:最基础的算法,其他算法都是对其的改进。
不足:效率问题,
标记
和清除
效率都不高。空间问题,会产生大量内存碎片。 -
复制算法
原理:将可用内存分为相同的两块,每次只使用一块。当这一块用完了,就将存活的对象复制到另一块上,然后把已经使用过的内存清理掉。
优点:不用关心内存碎片,实现简单,运行高效。 缺点:内存使用率低,不到总内存的一半;在对象存活率高的情况下,该方式效率低下。
当前的商业虚拟机都是用该算法回收新生代,但比例就不是1:1了,而是将内存分为较大的Eden Space和两块较小的Survivor Space。HotSpot默认Eden和Survivor比例为8:1,也就是只浪费10%的内存。而当Survivor空间不足时,需要使用老年代进行分配担保。
-
标记-整理算法
标记过程与
标记-清除算法
一样,而后续是将存活的对象向一端移动,而在端界外的内存占用都清理掉。 -
分代收集算法
当前商用虚拟机都采用分代收集算法。
思路:根据对象存活周期不同将内存分为几块(新生代、老年代),然后根据各代的特点选择最合适的收集算法。在新生代采用复制算法,因为每次收集有大量对象死去。在老年代次用
标记-清除算法
或标记-整理算法
。
-
-
HotSpot中的垃圾收集算法的实现?
暂无
-
垃圾收集器有哪些?
收集算法是内存回收的方法论,垃圾回收器是内存回收的具体实现。JVM中未规定垃圾回收器的实现。
-
Serial收集器
-
ParNew收集器
-
Parallel Scavenge收集器
-
Serial Old收集器
-
Parallel Old收集器
-
CMS收集器
-
G1收集器
-
-
什么是Serial收集器?
-
定位:最基础,最悠久的收集器。jdk1.3之前的唯一选择。
-
理论基础:使用复制算法的收集器
-
特点:单线程收集器;在工作时必须暂停其他工作线程。
-
外号:Stop the world
-
存在的意义:仍然受运行在客户端模式下的虚拟机默认的新生代收集器,简单高效。因客户端模式虚拟机分配内存不会太大(几十兆到一两百兆),因此收集时间短,相对来说最合适。
-
-
什么是ParNew收集器?
-
定位:Serial收集器的多线程版本。
-
理论基础:使用复制算法的收集器
-
缺点:单CPU下,
ParNew收集器
不会比Serial收集器
表现好。 -
存在的意义:运行在Server模式下虚拟机的首选新生代收集器,而且当前多线程中只有它能配合CMS一起工作。
-
-
什么是Parallel Scavenge收集器?
-
定位:JDK1.4后出现的新生代收集器。独立实现,没有使用传统GC收集器框架。
-
理论基础:使用复制算法的收集器
-
目标:达到一个可控制的吞吐量(CPU运行用户代码时间/CPU总运行时间)
-
应用:高吞吐量的收集器可以高效利用CPU的时间,主要适合在后台运算而不需要太多交互的任务。
-
控制吞吐量参数:
-XX:MaxGCPauseMillis
(控制停顿时间)和-XX:GCTimeRatio
(控制吞吐量大小)。 -
外号:“吞吐量优先”收集器
-
扩展:
-XX:+UseAdaptiveSizePolicy
参数设置后可以由虚拟机自动控制,动态调节参数。-- GC自适应的调节策略。
-
-
什么是Serial Old收集器?
- 定位:Serial收集器的老年代版本,也是单线程收集器。
- 理论基础:使用
标记-整理算法
的收集器 - 存在的意义:Client模式下的虚拟机使用。在Server模式下,主要有:1,与Parallel Scavenge搭配使用;2,作为CMS收集器的备用方案。在并发手机发生
Concurrent Mode Failure
时使用。
-
什么是Parallel Old收集器?
-
定位:
Parallel Scavenge收集器
的老年代版本,诞生于JDk1.6,使用多线程和标记-整理算法
。 -
理论基础:使用
标记-整理算法
的收集器。 -
意义:使得
Parallel Scavenge收集器
终于有了合适的应用组合,在注重吞吐量及CPU资源敏感场合,可以优先考虑使用Parallel Scavenge
+Parallel Old
组合。
-
-
什么是CMS收集器?
-
定位:JDK1.5之后推出,HotSpot上第一款真正意义上的并发收集器,实现了垃圾回收线程和用户线程同时工作。
-
目标:获取最短回收停顿时间。
-
应用:适合互联网站或者B/S系统的服务端,相应速度块,系统停顿时间短。
-
理论基础:
标记-清除算法
-
运作过程:初始标记、并发标记、重新标记、并发清除四步骤。初始标记和重新标记仍然需要"Stop the world",初始标记仅标记GCRoots能直接关联的对象,速度快。并发标记是进行GCRoots Tracing的过程。重新标记阶段则是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那部分对象的标记记录。停顿时间比初始标记稍长,但远比并发标记短。耗时最长的并发标记和并发清除可以与用户线程一起工作,所以总体看,CMS的内存回收是并发进行的。
-
优点:并发收集、低停顿。
-
缺点:
-
CMS收集器对CPU资源非常敏感。
-
CMS收集器无法处理浮动垃圾(在并发清除阶段产生的垃圾)。 需要预留内存空间提供并发收集时程序运行。
-
基于
标记-清除算法
,收集结束后会有大量内存空间碎片产生。在无法存储大对象时触发Full GC。 -
无法与
Parallel Scavenge收集器
配合使用。
-
-
-
什么是G1收集器?
-
定位:面向服务端应用的垃圾收集器,未来替代CMS收集器。正式商用与JDK1.7_4
-
特定:并行与并发;分代收集;空间整合(基于“标记-整理”算法,不会产生内存碎片);可预测的停顿。几乎就是实时java垃圾收集器。
-
内存布局:与之前其他收集器不同,将整个java堆分为多个大小相等的独立区域,虽然保留了新生代和老生代概念,但不再是物理隔离,都是一部分Region的集合。
难点:化整为零的思路,理解容易实现难。
在G1中,Region间的对象引用及其他收集器中新生代和老生代间的对象引用,虚拟机使用RememberedSet避免全堆扫描。Region与RememberedSet为1对1关系。
运作过程:初始标记、并发标记、最终标记、筛选回收。
-
-
G1建立可预测的停顿时间模型的原因?
因为G1收集器可以有计划的避免在整个Java堆中进行全区域的垃圾收集。G1跟踪各个Region里面垃圾堆积的价值大小 ,在后台维护优先列表。每次根据允许的收集时间,优先回收价值最大的Region。这种使用Region划分内存空间以及有优先级的区域回收方式,保证G1收集器在有限的时间内获取尽可能高的收集效率。
-
G1执行内存回收的详细过程?
初始标记节点仅仅只是标记下GCRoots能直接关联到的对象,并且修改TAMS(next top at mart start)的值,让下一阶段用户程序并发执行时,能在正确可用的Region中创建新对象,需要停顿线程,但耗时很短。
并发标记阶段是从GC Roots开始对堆中的对象进行可达性分析,找出存活对象,耗时时间长但可与用户线程并行执行。
最终标记阶段为了修正在并发标记期间因用户程序继续运作而导致标记产生变化的那一部分标记记录,虚拟机将这段时间对象变化记录在线程Remembered Set Logs中,在该阶段还需要将Remembered Set Logs中的数据合并到Remembered Set中。需要停顿线程,但可以并行执行。
筛选回收阶段首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间指定回收计划。给阶段也可并发执行,但因为时间可控,停顿用户线程可提高收集效率。
-
垃圾收集器各参数列表
-
内存分配与回收策略
-
对象优先在Eden分配
多数情况下,新生对象在新生代Eden区分配。当Eden没有足够空间时,将进行MinorGC。
-XX:+PringGCGetails
:参数打印内存回收日志。 -
大对象直接进老年代
大对象(需要大量连续分配内存空间的对象,如长字符串和数组),经常出现大对象容易导致内存还有空间时就会提前出发垃圾收集以获取连续空间。
-XX:PretenureSizeThreshold
令大于该设置阈值的对象直接进入老年代。避免在Eden和Survivor空间发生大量内存复制。 -
长期存活的对象将进老年代
虚拟机给每个对象定义对象年龄计数器,如果对象在Eden出生并经过一次MinorGC分配后仍然存活,将被移入到Survivor中,并且对象年龄置为1,每经过一次GC年龄加1,如果年龄达到设置阈值(默认为15),则晋升老年代。阈值可以通过
-XX:MaxTenuringThreshold
参数设置。 -
动态对象年龄判定
在Survivor空间中,如果相同年龄的所有对象大小总和大于Survivor空间一半,那将年龄更大或等于该年龄的对象直接进入老年代,不等待最大年龄阈值的触发。
-
空间分配担保
-