这篇文章经过翻译、总结,提炼SUN公司(已被甲骨文收购) 的文档 "Memory Management in the Java HotSpot™ Virtual Machine", Sun Microsystems, April 2006"java
获得的JVM内存管理方面的学习笔记。其中大部分插图来自文档。 原官方文档 查阅原文算法
1.内存的手动、自动管理安全
内存的手动管理,须要明确地指出对象什么时候再也不被使用并将其释放,而自动管理,须要根据对象的引用状况来判断释放与否。固然,被引用的对象不必定都有用;混乱的引用致使了一些对象不会被释放。自动管理解决了大部份内存问题,但没法彻底解决内存问题。服务器
2.垃圾回收的概念网络
垃圾回收器(Garbage Collector, GC) 是JVM 进行内存管理的工具。多线程
垃圾回收器负责:并发
Java程序中被引用的对象(应该指被有效引用的对象)称为存活的(live). 再也不被引用的对象称为死亡的(dead) 也称做垃圾(garbage). 对死亡的对象进行查找并释放内存空间的过程被称为垃圾回收。oracle
2.1.什么是良好的垃圾回收器?工具
安全即有用的数据不会被回收,无用的数据不会长期得不到释放;高效即回收器工做时不会引发长时间的卡顿。性能
然而,大多数系统都有时间,空间和频率之间的权衡。堆小会提高回收速度,但堆很快会被填满,形成更频繁地回收。
在对象的被释放后,回收获得的空间多是分散在不一样区块的碎片,这些碎片可能都没有足够的空间来分配一个大对象。咱们须要消除碎片得到连续的空间,消除碎片的方法成为“压实”(compaction)。
分配与回收是否成为多线程应用程序在多处理器系统上扩展的瓶颈。
2.2.垃圾回收算法要权衡什么?
设计或选择一个垃圾回收算法应作出以下选择:
串行则只有一个CPU进行回收,并行则将回收任务被分割交由不一样的CPU同时执行。并行虽迅速,但会产生额外的复杂性与潜在的碎片。
“暂停”则回收执行时,应用程序的将彻底暂停执行。同步则经过增大开销来缩短暂停时间以改善性能,同步可与应用程序同时进行,提升响应速度,但面对对象更新时要格外当心。
“压实”则在回收工做后将全部存活的移动至一块儿,彻底回收剩余内存。以后将指针指向第一个自由位置,以便快速分配对象,以后更新自由位置用于下一次分配。“非密实”在对象释放后不进行处理,以快速完成回收任务,所以会产生潜在碎片。复制能够将存活的对象“疏散”到不一样的内存区域,原区能够被快速利用进行下一次分配,但其须要额外的时间和空间。
3.从J2SE 5.0 HotSpot JVM起的四种垃圾回收器
3.1.HotSpot JMM概况
HotSpot JMM 存在三个区块:年轻代(Young Generation)、年老代(Old Generation)、持久代(Permanent Generation), 大部分对象在初始化后被分配到年轻代的Eden Space, 当Eden Space 将饱和时,经过Survivor Space 进行回收,屡次回收后幸存的对象会被移动到年老代上的养老区(Tenured Space). 经历屡次年轻代垃圾回收后的幸存对象被分配到年老代。空间占用大的对象能够直接分配进入年老代。持久代通常存储可被垃圾回收器直接识别为持久的对象,例如类和方法以及描述类。
3.2.垃圾回收的类型
年轻代将饱和时,执行年轻代回收(Young Generation Collection), 亦称小回收(Minor Collection). 年老代或持久代将饱和时,执行全面回收(Full Collection),亦称主回收(Major Collection) 对全部代对象的进行回收。一般,年轻代的对象先被回收,为保障高效,年轻代回收使用特定的算法。而年老代和持久代则使用相同的算法。
3.3.Serial Collector (SC 串行回收器)
SC由单个线程负责执行,回收时应用程序的执行将被暂停。
回收执行时,在年轻代的Eden Space 中存活的对象被复制到Survivor Spaces 的To Space 中,但若是该对象大小超过To Space 的空间,它将被直接复制到年老代。Survivor Spaces 中From Space 存活的对象仍然被认为是年轻的,所以也会被复制到To Space 中,直到对象被标记为年老时才会被复制到年老代。当To Space 快满时,Eden Space 和From Space 中的幸存对象不会被复制进去,不管经历了多少年轻代的垃圾回收。当Eden Space 和From Space 中幸存对象全被复制后,遗留对象(图中标记为红色X)所有被标记为死亡,再也不被检查与标记。年轻代回收完毕后,Eden Space 和From Space 将清空;若有没进入年老代的对象,那它只能在To Space 中,此时From Space 和To Space 交换角色。
SC在年老带中采用mark-sweep-compact算法进行垃圾回收。
(1) 标记(mark):判断哪些对象仍然存活并进行标记
(2) 扫描(sweeps):经过对若干代的扫描肯定垃圾
(3) “滑动—压实”(sliding-compaction):对幸存对象进行压缩,以使他们在连续的空间上有序并至地排列。“压实”操做后,碎片被整合集中,使新进入的对象可使用凹凸指针(bump-the-pointer)进行快速地址分配。
多用于客户机(client-style). 程序能够容忍短暂地暂停。在J2SE 5.0以后的版本中默认开启。
指令配置:-XX:+UseSerialGC
3.4.Parallel Collector (PC 并行回收器)
PC是高吞吐量回收器(Throughput Collector), 具备充分利用核心的优点。当前应用普遍。PC是SC的增强版本。
PC与SC采用相同的方式对年轻代进行垃圾回收,还是一个“‘暂停—复制’回收器”,但并行下降了时间开销,提升了应用的吞吐量。
与SC相同,采用mark-sweep-compact回收算法提高效率,但仍有罕见状况使年老代的回收形成程序较长时间的暂停。
多用于服务器(server-class). 在J2SE 5.0以后的版本中默认开启。
指令配置:-XX:+UseParallelGC
3.5.Parallel Compacting Collector (PCC 并行压缩回收器)
与PC相同,PCC在年老代垃圾回收采用了新算法,并最终取代PC。
PCC与PC采用相同的方式对年轻代进行垃圾回收。
PCC的年老带回收会形成应用暂停。
(1) 标记(mark):在逻辑层面上将年老代划分红大小固定的分区。经过多线程先标记根集合中能够直接访问的对象,再标记剩余对象。只要区中有幸存对象,该区的大小和位置信息将会被采集更新。
(2) 总结(summary):该方法是对区进行操做,而非操做对象。一般状况下,当进行年老代垃圾回收时,因为前某一代的“压实”操做,使得当前内存空间中,左边的存储密度仍然很大,甚至左边的对象几乎是连续排列的。从高密度区中回收零星的碎片空间可能不值得再次“压实”形成的开销。因此,总结会从最左端检查区的密度(依据标记后区的信息采集结果), 直到到达一个点,从该点到最右端进行“压实”回收内部的碎片空间能够弥补开销。这个点被成为“密度前缀”(dense prefix), 它左边的对象均不会被位移而右边的空间均会被压缩回收。
总结方法实际是串行实现的,固然也能够并行,但并无标记和“压实”的并行实现那样重要。
(3) “压实”(compaction):多个线程基于总结操做的结果,肯定须要被填满的区,而后独立复制数据进行填充。最后,堆的内存分配是一端十分密集,另外一端几乎为空。
多线程的机器,但不适合大型共享机器。
应用前需指明。
指令配置:-XX:+UseParallelOldGC
3.6.Concurrent Mark-Sweep (CMS) Collector (CMSC 并发标记回收器)
在不少应用场景中,快速响应(fast response time) 比端到端的吞吐量(end-to-end throughput) 更重要。若是使用了大堆,年老代的回收可能形成长时间停顿。
HotSpot JVM 引入了CMSC 亦做“低延迟回收器”(low-latency collector) 来解决这一问题。
CMSC与PC采用相同的方式对年轻代进行垃圾回收。
大多数的CMSC垃圾回收是在应用程序执行时完成的。
(1) 初始标记(initial mark):应用暂停,标记根集合中能够直接访问的对象,得到幸存对象的初始集合。
(2) 并发标记(concurrent mark):标记初始集合中所有被间接引用的对象。
(3) 备注/再标记(remark):因为并发标记不会暂停应用,没法保证被更新的引用都获得标记。为此,备注过程再次暂停应用,对并发标记时被修改的对象进行标记,以保障垃圾回收的完整性。因为备注所需的暂停时长远大于初始标记的暂停时长,采用多线程来提升效率。
(4) 扫描(sweep):CMSC加大备注过程的开销来减小暂停时长,而且是惟一没有“压实”操做的回收器,即不会将幸存的对象有序并至排列。
虽然非“压实”节约时间,但因为空闲内存不连续,回收器没法使用凹凸指针技术分配新对象。为此,CMSC用多个链式队列(应该是对碎片依据空间大小分类存放)碎片,根据新对象的大小经过链式队列分配适当位置。所以,相比于其余回收器,CMSC在年老代的资源分配成本更高,也须要开辟更大的堆。
另外,尽管老年代的每次回收对所有对象进行清洗,但以后的“新生垃圾”,亦做浮动垃圾(floating garbage), 只能在下一次老年代回收时被清理。因为没有“压实”,碎片早晚会积累,为此CMSC对活跃对象进行跟踪(track), 经过估算空间需求来分割或合并碎片空间。
CMSC适用于要求暂停时间短,而且能够承担与回收器共享处理器资源的应用。一般适用于多线程,老年代数据集较大的应用,如网络服务器。
应用前需指明。
指令配置:-XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode (incremental mode)