Fork me on GitHub

分代垃圾收集

垃圾收集器基于弱分代假设
1. 大多数分配对象的存活时间很短
2. 存活时间久的对象很少引用存活时间短的对象
新生代空间小且收集发生频繁,新生代大部分对象存活时间短,新生代收集(Minor GC)之后存活的对象很少。由于Minor GC专注于小对象且有大量垃圾对象的空间,所以收集效率很高。
老年代新生代长期存活的对象会被晋升(Tenure)到老年代,老年代收集(Full GC)发生频率低,但是发生时间长。
垃圾收集器不需要扫描整个老年代就可以知道新生代中存活的对象,因为老年代中使用了Card Table结构用来保存老年代对象对新生代的引用状态,这个Card Table会在新生代对象字段发生变化时更新是否为脏卡,Minor GC会只扫描老年代中的脏卡,所以效率很高。

新生代

  1. Eden 大多数新对象分配在这里,如果是大对象直接分配到老年代,Eden几乎总是空的。
  2. Survivor 这个有一对,分别具有From To两个角色,这里存放的对象至少经历过了一次Mnior GC,他们在晋升到老年代之前还有一次被回收的机会,当发生Mnior GC后这一对空间的角色会发生互换,From表示保存存活的对象,To表示空的空间。
    由于Minor GC收集过程中复制存活的对象,这种垃圾收集器称为复制垃圾收集器。当Minor GC中Survivor可能不足以容纳Eden和另一个Survivor中存活对象,如果出现存活对象溢出,多余的对象会被过早的晋升到老年代,这会导致老年代中短期存活对象的增长从而引发性能问题。当在Minor GC后对象溢出发生后会进行Full FC,这会导致遍历整个Java堆,这称为提早失败

快速分配内存

垃圾收集器以复制方式回收HotSpot VM新生代,其好处在于回收以后Eden总为空,在Eden中运用被称为指针碰撞的技术就可以有效的分配空间。应用多是多线程的,内存分配的操作需要考虑多线程安全,如果只用全局锁,在Eden中的分配操作就会成为瓶颈因而降低性能,HotSpot VM使用了线程本地分配缓冲区(TLAB)技术,为每个线程设置各自的缓冲区,即Eden的一下块,以此来改进多线程分配的吞吐量。这样使用指针碰撞技术快速分配不需要使用锁。当TLAB被填满时需要获取新的空间,就需要采用多线程安全的方式。

老年代

老年代采用标记清除压缩收集算法

Serial 收集器

Minor GC和Full GC都是以Stop-The-World方式运行,只有等垃圾收集结束后,应用程序才会继续执行。适合对停顿不敏感或者客户端应用。

Parallel收集器

吞吐量为先的收集器。新生代采用Stop-The-World方式收集,老年代采用标记-压缩方式。Minor GC和Full GC都是并行的,使用所有可用的处理器资源。

Mostly-Concurrent收集器

低延迟为先。有的应用需要快速响应,在Stop-The-World模式中,应用线程在垃圾收集开始时停止运行,直到垃圾收集结束后才继续运行和处理外部请求。Minor GC通常不会导致长时间的停顿。
HotSpot VM引入了Mostly-Concurrent收集器,也称为并发标记清除收集器(CMS),它管理新生代的方式和Parallel收集器和Serial收集器相同,但在老年代则是尽可能的并发执行,每个垃圾收集周期只有2次短的停顿,这是因为它有2个标记阶段,初始标记和重新标记。
与Parallel收集器相比CMS老年代停顿变短了,但代价是新生代停顿略微拉长,吞吐量有所降低,堆的大小有所增长,并且由于并发,垃圾收集会占用应用CPU周期,这适合快速响应的(Web服务器)应用。

G1收集器

他是CMS的替代者。G1是一个并行、并发和增量式压缩停顿的垃圾收集器。G1整体上没有划分成新生代和老年代,它将Java堆分成相同尺寸的块(区域 Region)。G1的名字由来是优先收集垃圾最多的区域。

影响垃圾收集的条件

  1. 内存分配 当Eden满时,就会发生Minor GC;当老年代占用超过CMS初始限额就会发生CMS,应用的内存分配速率越高,垃圾收集的触发就越频繁。不合适的数组类数据结构尺寸,如果ArrayList初始尺寸太小,它内部的数组随后可能调整尺寸几次,导致不必要的内存分配。
  2. 存活数据的多少 Java堆中存活对象越多,收集器需要做的工作越多。不好的编程实践,对象池化,它是长时间存活的,因此他们会增加老年代存活数据的尺寸。
  3. 老年代中引用的更新


最新评论

    还没有人评论...

当当

友情链接

Powered by Python. Copyright © 2017.

鄂ICP备17010875号. All rights reserved.