JVM内存管理笔记

这篇文章经过翻译、总结,提炼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.垃圾回收算法要权衡什么?

设计或选择一个垃圾回收算法应作出以下选择:

  • 串行(Serial)与并行(Parallel)

串行则只有一个CPU进行回收,并行则将回收任务被分割交由不一样的CPU同时执行。并行虽迅速,但会产生额外的复杂性与潜在的碎片。

  • 同步(Concurrent)与“暂停”(Stop-the-world)

 “暂停”则回收执行时,应用程序的将彻底暂停执行。同步则经过增大开销来缩短暂停时间以改善性能,同步可与应用程序同时进行,提升响应速度,但面对对象更新时要格外当心。

  • “压实” (Compacting)与“非密实” (Non-compacting)与复制(Copying)

“压实”则在回收工做后将全部存活的移动至一块儿,彻底回收剩余内存。以后将指针指向第一个自由位置,以便快速分配对象,以后更新自由位置用于下一次分配。“非密实”在对象释放后不进行处理,以快速完成回收任务,所以会产生潜在碎片。复制能够将存活的对象“疏散”到不一样的内存区域,原区能够被快速利用进行下一次分配,但其须要额外的时间和空间。

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由单个线程负责执行,回收时应用程序的执行将被暂停。

  • 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的年老代回收机制:

SC在年老带中采用mark-sweep-compact算法进行垃圾回收。

(1) 标记(mark):判断哪些对象仍然存活并进行标记

(2) 扫描(sweeps):经过对若干代的扫描肯定垃圾

(3) “滑动—压实”(sliding-compaction):对幸存对象进行压缩,以使他们在连续的空间上有序并至地排列。“压实”操做后,碎片被整合集中,使新进入的对象可使用凹凸指针(bump-the-pointer)进行快速地址分配。

  • SC的应用

多用于客户机(client-style). 程序能够容忍短暂地暂停。在J2SE 5.0以后的版本中默认开启。

指令配置:-XX:+UseSerialGC

3.4.Parallel Collector (PC 并行回收器)

PC是高吞吐量回收器(Throughput Collector), 具备充分利用核心的优点。当前应用普遍。PC是SC的增强版本。

  • PC的年轻代回收机制:

PC与SC采用相同的方式对年轻代进行垃圾回收,还是一个“‘暂停—复制’回收器”,但并行下降了时间开销,提升了应用的吞吐量。

  • PC的年老代回收机制:

与SC相同,采用mark-sweep-compact回收算法提高效率,但仍有罕见状况使年老代的回收形成程序较长时间的暂停。

  • PC的应用

多用于服务器(server-class). 在J2SE 5.0以后的版本中默认开启。

指令配置:-XX:+UseParallelGC

3.5.Parallel Compacting Collector (PCC 并行压缩回收器)

与PC相同PCC在年老代垃圾回收采用了新算法,并最终取代PC

  • PCC的年轻代回收机制:

PCC与PC采用相同的方式对年轻代进行垃圾回收。

  • PCC的年老代回收机制

PCC的年老带回收会形成应用暂停。

(1) 标记(mark):在逻辑层面上将年老代划分红大小固定的分区。经过多线程先标记根集合中能够直接访问的对象,再标记剩余对象。只要区中有幸存对象,该区的大小和位置信息将会被采集更新。

(2) 总结(summary):该方法是对区进行操做,而非操做对象。一般状况下,当进行年老代垃圾回收时,因为前某一代的“压实”操做,使得当前内存空间中,左边的存储密度仍然很大,甚至左边的对象几乎是连续排列的。从高密度区中回收零星的碎片空间可能不值得再次压实”形成的开销。因此,总结会从最左端检查区的密度(依据标记后区的信息采集结果), 直到到达一个点,从该点最右端进行“压实”回收内部的碎片空间能够弥补开销。这个点被成为“密度前缀”(dense prefix), 它左边的对象均不会被位移而右边的空间均会被压缩回收。

总结方法实际是串行实现的,固然也能够并行,但并无标记和“压实”的并行实现那样重要。

(3) “压实”(compaction):多个线程基于总结操做的结果,肯定须要被填满的区,而后独立复制数据进行填充。最后,堆的内存分配是一端十分密集,另外一端几乎为空。

  • PCC的应用

多线程的机器,但不适合大型共享机器。

应用前需指明。

指令配置:-XX:+UseParallelOldGC

3.6.Concurrent Mark-Sweep (CMS) Collector (CMSC 并发标记回收器)

在不少应用场景中,快速响应(fast response time) 比端到端的吞吐量(end-to-end throughput) 更重要。若是使用了大堆,年老代的回收可能形成长时间停顿。

HotSpot JVM 引入了CMSC 亦做“低延迟回收器”(low-latency collector) 来解决这一问题。

  • CMSC的年轻代回收机制:

    CMSC与PC采用相同的方式对年轻代进行垃圾回收。

  • CMSC的年老代回收机制:

大多数的CMSC垃圾回收是在应用程序执行时完成的。

(1) 初始标记(initial mark):应用暂停,标记根集合中能够直接访问的对象,得到幸存对象的初始集合。

(2) 并发标记(concurrent mark):标记初始集合中所有被间接引用的对象。

(3) 备注/再标记(remark):因为并发标记不会暂停应用,没法保证被更新的引用都获得标记。为此,备注过程再次暂停应用,对并发标记时被修改的对象进行标记,以保障垃圾回收的完整性。因为备注所需的暂停时长远大于初始标记的暂停时长,采用多线程来提升效率。

 

 

 

 

 

 

 

 

 

 

 

 

 

(4) 扫描(sweep):CMSC加大备注过程的开销来减小暂停时长,而且是惟一没有“压实”操做的回收器,即不会将幸存的对象有序并至排列。

虽然非“压实”节约时间,但因为空闲内存不连续,回收器没法使用凹凸指针技术分配新对象。为此,CMSC用多个链式队列(应该是对碎片依据空间大小分类存放)碎片,根据新对象的大小经过链式队列分配适当位置。所以,相比于其余回收器,CMSC在年老代的资源分配成本更高,也须要开辟更大的堆。

另外,尽管老年代的每次回收对所有对象进行清洗,但以后的“新生垃圾”,亦做浮动垃圾(floating garbage), 只能在下一次老年代回收时被清理。因为没有“压实”,碎片早晚会积累,为此CMSC对活跃对象进行跟踪(track), 经过估算空间需求来分割或合并碎片空间。

  • CMSC的应用:

CMSC适用于要求暂停时间短,而且能够承担与回收器共享处理器资源的应用。一般适用于多线程,老年代数据集较大的应用,如网络服务器。

应用前需指明。

指令配置:-XX:+UseConcMarkSweepGC       -XX:+CMSIncrementalMode (incremental mode)

相关文章
相关标签/搜索