Java内存管理基础

JerryXia 发表于 , 阅读 (0)
2016 年 02 月 20 日
jvm

众所周之,Java最强大的地方之一就是其自动内存管理机制,也就不需要像C/C++语言那样,需要开发人员手动进行内存管理,需要时刻关注内存应该何时正确地被释放自动内存管理机制将程序员从内存管理的凶险解脱出来,这将大大提升Java开发人员的生产力,但作为Java开发人员,即使不用直接面对内存管理,但却应该对JVM的内存管理机制有所掌握,对自己的编程能力也会有潜移默化的提升。本文将对JVM内存管理进行一番解读,实现主要是HotSpot Virtual Machine(J2SE 1.5)

手动内存管理 VS 自动内存管理

内存管理的一个主要目标就是识别那些分配的对象不再需要,并且释放掉其所占用的内存空间,以供后续的内存分配使用。有一些编程语言,如C++,开发人员手动管理内存,这将很容易出现很多引起程序错误和崩溃的问题,比如悬挂引用内存泄漏等,因此开发人员需要花费大量时间来调试和修复。

现在大多数现代面向对象编程语言中,已被广泛应用的另一种内存管理方法:自动内存管理, 其通过垃圾收集器(Garbage Collector)来实现内存管理。只要对象还有被引用,垃圾收集器将不会对其进行收集,从而避免了悬挂引用问题,一旦对象不再被引用,垃圾收集器将会在合适的时候对其进行收集,并释放对应的内存空间,因此避免了内存泄漏问题。

垃圾收集原理

通常垃圾收集器总体需要负责三件事情: 为对象分配内存保证任何被引用的对象仍在内存中清理程序中不可达的对象。当为新对象分配内存时,需要在堆中找出具有一定大小且未被使用的内存块,这是一件比较困难的事情,大多数动态内存分配算法的主要困难在于避免内存碎片化,并且同时需要保证内存分配和释放的性能

理想的垃圾收集器

垃圾收集器必须是安全且全面的。也就是说,存活对象绝不能被错误释放,并且不允许垃圾对象经过多个垃圾收集周期仍然未被释放

垃圾收集器也应该能够有效地运行,以至于不应该导致应用暂停太长时间。然而,对于大多数计算机相关的系统,都需要在时间空间和频率等因素作出一些权衡。例如,若JVM堆设置过小,虽然每次垃圾收集会变得更快,但是总的垃圾收集次数也会更多;若JVM堆设置过大,虽然总的垃圾收集次数有所减少,但每次垃圾收集耗时也会更长。

垃圾收集器也需要针对内存碎片作一些处理,在垃圾收集过程中,对一些小对象回收后,有可能会产生很多空间较小的内存块,这些小内存块不能容纳下大对象,最坏的情况也就是即便内存还未使用完,但却不能再分配对象。清理碎片的过程叫压缩(Compaction)

另外,可扩展性也是相当重要的,在多处理器系统中,对于多线程应用,内存分配不应该成为可扩展性瓶颈收集操作也不应该成为这样的瓶颈。

如何设计或选择垃圾收集器

在设计或选择垃圾收集器时,通常需要作出一些选择:

串行 VS 并行

对于串行收集,同一时刻只能作一件事,比如,即使有多个CPU可用时,只有其中一个才能用于执行垃圾收集。当使用并行收集时,垃圾收集任务将被分成若干个子任务,并同时在不同的CPU上执行,并行执行使得垃圾收集执行得更快,但这也导致了额外的复杂度和内存碎片问题

并发 VS Stop-the-world

stop-the-world垃圾收集正在执行时,整个应用将被挂起,这对于强交互应用(要求低暂停时间短)并不友好,比如Web应用。相比stop-the-world,有一些垃圾收集器的收集任务可以与应用同时执行。典型的并发垃圾收集器会并发执行大部分垃圾工作,但仍然会有短暂的stop-the-world。显然,Stop-the-world垃圾收集并发垃圾收集更简单,因为在整个收集过程中,JVM堆会被冻结,因而对象的状态将不会发生改变,但缺点也很明显,收集过程会挂起应用。相对于使用并发垃圾收集器时,应用暂停时间将被缩短,但需要额外注意一些细节,垃圾收集器在操作对象时,与此同时,有可能应用程序也在操作相同的对象,为了保证垃圾收集器不会错误地回收对象,则要增加一些额外影响性能的工作,并且需要更大的堆内存。

压缩 VS 复制

垃圾收集器已经决定哪些对象存活及哪些对象需要回收之后,可以进行内存压缩,即将所有存活的对象移动到一起(连续),然后完全释放掉需要回收的内存。内存压缩之后,就能够很容易且很快地将新对象分配在第一块空间内存中了,一个简单的指针就能跟踪下一个内存分配的位置。与内存压缩型收集器相比,非内存压缩收集器则利用额外的内存空间来回收垃圾对象,即不会像内存压缩型收集器一样将所有存活对象移动一起,以便释放出一大块空闲内存,这样的好处是垃圾收集时间更短,缺点则是潜在的内存碎片问题。通常来说,从堆中分配对象比压缩堆更昂贵,因为为了分配新对象,可能有必要在堆中寻找一段足够大的连续空间。第三个可选的则是复制收集器,即复制存活的对象到另一个内存区,这样的好处是保持源内存区是空闲的,可以很容易且很快地用于后续的对象分配,但缺点是需要额外的时间来执行复制操作额外的内存空间

性能指标

通常会使用几个指标来评估一个垃圾收集器的性能,包括: