Java内存管理-- GC(Garbage Collection)的基本概念 --Hotspot的分代回收

来源:互联网 发布:华为软件测试流程 编辑:程序博客网 时间:2024/05/17 08:24

引用:http://www.daniel-journey.com/

可能会对原文加上个别注释,用红色标识出来

==================================================

这是Java内存管理系列文章的第一篇。

GC的概念

GC是一种自动内存管理程序,与之相对应的是C++采用的内存管理方式。GC主要的职责就是分配内存;保证被引用的对象始终在内存中;把不被应用的对象从内存中释放。被引用的对象称之为Live 对象;不被引用的对象就是Dead对象,是需要回收的。(引出概念弱引用,weak reference)任何事物都有光面和黑暗两面,原因很简单GC是一个很复杂的东西:-)。

GC会自动计算对象被引用的情况,只要对象不在被引用,相应的内存就会被回收,而C++中需要开发人员通过代码来“显示”地回收内存,如果程序员没有回收就会导致内存的泄露(内存泄露的原因有很多种这只是其中一个)。C++中还有经常出现的一个问题是一个对象在还有其他引用存在的情况下,就被程序给回收了,导致其他引用访问该对象时出现严重错误。另外,GC非常重要的一点就避免内存碎片,道理跟windows的磁盘整理一样,把使用中各个内存块整合起来,这样才能保证有足够的空间来存储大对象。

理想的GC

一个理想的GC要能够满足以下几点:

  • 该回收的回收,不改回收的绝不回收
  • GC要快而且GC运行的时候不能导致应用程序的停顿。
  • 限制内存碎片,对象被回收以后,所使用的内存会被回收,如果不加处理内存中就会出现大量的内存碎片,这样就有可能导致因为没有足够的连续空间分配给某些大对象而导致OutofMemory。消除内存碎片的的手段之一就是“内存压缩”
  • 可扩展性(Scalability),内存的分配和回收都不能成为应用程序的瓶颈。

GC的设计选择

  • 串行回收(Serial)VS并行回收(Parallel)。串行就是不管有多少个CPU,始终只有一个CPU用来执行回收操作,而并行就是把整个回收工作拆分成多个,由多个CPU同时执行。并行回收执行会快,但复杂度增加,另外也有其他一些副作用,比如内存碎片会增加。
  • 并发执行(Concurrent)VS应用程序停止(Stop-the-world)。Stop-the-world的GC方式在执行GC的同时会导致应用程序的暂停。并发执行的GC虽然不会导致应用程序的暂停,但由于并发执行GC要解决和应用程序的执行冲突(应用程序可能会在GC的过称中修改对象),并发执行GC执行的消耗会高于Stop-the-world,而且执行也需要更多的内存堆。
  • 压缩(Compacting)VS不压缩(Non-compacting)VS拷贝(Copying)。为了减少内存碎片,支持压缩的GC会把所有的活对象搬迁到一起,然后将之前占用的内存全部回收。不压缩式的GC顾名思义就是在GC的过程中不压缩内存,较之压缩式的GC,不压缩式的GC回收内存快了,而分配内存慢了,而且无法解决内存碎片的问题。拷贝式的GC会将活对象拷贝到不同的内存区域中,这种方式的优点是源数据可以被认为已经清空并可以用来分配,缺点也很明显,需要拷贝数据和额外的内存。

GC的性能评判标准

  • 生产力(Throughput)—全部时间中不用于GC的比例。
  • GC的开销—全部时间中用于GC的比例。
  • 暂停时间—GC过程中应用程序执行暂停的时间。
  • GC的频率—通过跟应用程序的执行比较来得到GC的执行频率。
  • 支持GC运行所需使用的内存大小—例如heap的大小。
  • GC的及时性(Promptness)—一个对象从被废弃到内存被回收之间的时间差。

这是Java内存管理系列文章的第二篇,上一篇是Java内存管理之基础概念——GC(Garbage Collection)的基本概念。

如果大家读过本系列的第一篇文章Java内存管理之基础概念——GC(Garbage Collection)的基本概念就会理解到实现一个优秀的GC算法的确是一个很大的挑战。Hostspot虚拟机采用了“分代回收”的策略,而“分”的非常重要的一个依据就是根据对象存在的时间的长短分成若干个“代(Gerneration)”,每个代上可以采取不同的GC策略。而采用这种“分代回收”策略是利用了2条潜规则,而且这两条潜规则不只限于Java

  • 绝大对数的对象不会被长时间引用,这些对象在他的“青年期”就会被回收。
  • 几乎不存在很老和很新对象之间的引用

依据这两条潜规则,Hotspot分离出新生代(Yound Generation)和旧生代(Old Generation)。由于新生代的空间通常都比较小而且可能存在大量不再被引用的对象,所以针对新生代的GC执行频率高、速度快

在新生代中存在了一定时间还没被GC掉的对象最终会被提升到旧生代。旧生代空间比新生代的要大,但它的占用率增长会比较缓慢,因此,旧生代的GC执行频率低,但需要更长的时间来完成。


对新生代的GC侧重的是速度而且执行频繁,与此相反旧生代的GC侧重的是空间的利用率,即便在旧生代GC频率低的情况下依然要能够正常地工作。

另外还有一个永生代(Permanent Generation),永生代中的保存的对象都是JVM用来方便管理GC的,例如类和方法对象以及它们的描述对象。(Sun的JVM不回收PermGen,由于动态语言产生类会比较多,有时就会出现PermGen Overflow。另外推荐用JRockit JVM)

新生代由一个Eden区域和2个survivor空间构成。绝大多数对象先分配到Eden中(有一些大的对象会可能会直接分配到旧生代中),survivor空间中的对象至少经历过一次新生代的GC,所以这些对象在被转移到旧生代之前都先暂且保留在survivor空间中。同一时间两个survivor空间中有一个用来保存对象,而另一个是空的,用来在下次的新生代GC中保存对象。


原创粉丝点击