Material Render Phone

Tomxin7

“工欲善其事,必先利其器”,作为Java核心之一的JVM,是一个Java开发者必须要去理解的部分,在校招的时候,走马观花式的过了一遍,很多东西没有深入理解,所以打算再看一遍《深入理解JAVA虚拟机》,把其中重要的部分整理出来,记录在博客之中。

垃圾回收

判断对象是否存活

引用计数法

在对象中添加一个引用计数器,当有地方引用这个对象的时候,引用计数器+1,当引用失效的时候,计数器-1。因为存在循环引用,所以引用计数法很少被使用。

可达性分析算法

在Java中,是通过可达性分析来判定对象是否存活的。引用链(GC Roots)的对象作为起点,从这些节点开始向下搜索,当一个对象到GC Roots没有任何引用链相连时(即从GC Roots节点到该节点不可达),则证明该对象是不可用的。

可以作为GCRoots的对象:
1. 虚拟机栈
1. 方法区的类属性所引用的对象
1. 方法区中常量所引用的对象
1. 本地方法栈中所引用的对象
### 垃圾回收算法

标记-清除算法

标记清除算法是其他算法的基础,首先标记出所需要回收的对象,在标记完成后统一回收被标记的对象,它有两个问题,一是效率问题:标记和清除的效率都不高,二是空间问题,标记清除后会产生大量不连续的空间,空间碎片过多会导致要分配大对象空间的时候,空间不够,需要再次提前触发一次垃圾回收。

复制算法

传统的复制算法:

解决了效率的问题,把内存分为两块,每次使用一块,当这一块内存用完了,将还存活的对象复制到另一块,然后将已使用过的内存一次清理掉,这样使得每次都是对半块内存进行回收,但是每次只有一半的内存可以使用,代价太高了。

商业虚拟机的复制算法:

IBM研究表明,新生代对象中98%是“朝生夕死”,所以不需要按照1:1的比例来划分内存空间,而是将内存分为一块打的Eden空间和两块较小的Survivor空间。当回收时,将Eden和Survivor中存活的对象一次性复制到另一块Survivor上,最后清理Eden和Survivor的空间、HotSpot默认Eden和Survivor的比例是8:1,当Survivor空间不够用时,需要依赖老年代进行担保分配。

标记-整理算法

在老年代中,大部分都是存活的对象,如果使用复制算法,存活对象的复制需要较多的操作,效率会变低。极端的情况是百分之百都存活,需要全部复制。标记-整理算法是让所以存活的对象向一端移动,然后清理掉边界以外的内存。

分代收集算法

当前的商业虚拟机都是用“分代收集”算法,根据对象的存活周期的不同将内存划分为几块,一般是把java堆分为新生代和老年代,这样可以根据不同年代采取适当的收集算法。

垃圾回收器

并发和并行的区别:
1. 并发:边扔垃圾边清理;
1. 并行:不同时进行,但是多人清理垃圾

Serial

  1. 历史最悠久的收集器,JDK1.3之前是新生代收集的唯一选择
  2. 单线程的收集器,而且在垃圾清理的时候,必须暂停其他所有线程的工作
  3. 现在的使用场景的客户端,因为桌面应用场景分配给虚拟机的内存一般不会很大,收集起来也快。
  4. 新生代收集器

Parnew

  1. Parnew是Serial的多线程版本,在单核CPU的情况下性能可能还没有Serial好。
  2. 除了Serial之外,只有Parnew可以和CMS共同使用。
  3. 新生代收集器

Parallel Scavenge

  1. 新生代收集器,使用复制算法
  2. 并行的多线程收集器
  3. 关注点是达到一个可控制的的吞吐量。
  4. XX:MaxGCPauseMillis 垃圾收集器最大的停顿时间,单位毫秒
  5. XX:GCTimeRatio 吞吐量大小,大于0小于100

Cms

  1. 老年代收集器,使用标记清除算法
  2. 优点:并发收集。低停顿
  3. 缺点:占用大量CPU资源,无法处理浮动垃圾,出现Concurrent Mode Failure
  4. 垃圾收集分为四个步骤:
    1. 初始标记(CMS initial mark)
    2. 并发标记(CMS concurrent mark)
    3. 重新标记(CMS remark)
    4. 并发清除(CMS concurrent sweep)

其中初始标记、重新标记这两个步骤仍然需要“Stop The World”。初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快,并发标记阶段就是进行GC Roots Tracing的过程,而重新标记阶段则是为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。

由于整个过程中耗时最长的并发标记和并发清除过程中,收集器线程都可以与用户线程一起工作,所以总体上来说,CMS收集器的内存回收过程是与用户线程一起并发地执行的。通过图3-10可以比较清楚地看到CMS收集器的运作步骤中并发和需要停顿的时间。

G1

特点:
1. 并行与并发:G1可以充分利用多CPU,部分其他收集器需要停顿java线程进行GC动作,G1可以通过并发的方式让java程序继续执行。
1. 分带收集:G1不需要其他收集器配合就能独立管理整个GC堆。
1. 空间整合:G1整体看起来是基于“标记-整理”算法实现,从局部看起来是基于“复制”算法实现。这些特性有利于程序的长时间运行,分配大对象不会因为无法找到连续的空间提前下一次GC。
1. 可预测的停顿:这是与CMS的另一大优势,G1除了降低停顿以外,还能建立可预测的停顿时间模型。

在G1之前,其它收集器进行收集的范围都是整个新生代或者老年代,而G1收集器却与之有很大差别,他将整个java堆划分为多个大小相等的独立区域(region),虽然还保留有新生代和老年代的概念,但是其不再是物理隔离的了,都是一部分region的集合。

G1可以建立可预测的停顿时间模型,是因为他可以避免在整个java堆中进行垃圾回收,G1在后台维护一个优先列表计算Region中垃圾回收价值大小,他每次先回收价值大的。G1中每个Region都有一个对应的Remembered Set,当进行内存回收时,在GC根节点的枚举范围加入Remenbered Set可以保证不对全堆扫描,也不会有遗漏。

如果不计算维护Remembered Set的操作,G1大致分为以下几个步骤:
1. 初始标记 :Initial Marking
1. 并发标记 :Concurrent Marking
1. 最终标记 :Final Marking
1. 筛选回收 :Live Data Counting and Cleanup

转载声明:写作不易,商业转载请联系作者获得授权,非商业转载请注明出处,并附上原文链接,感谢!