4款Java垃圾回收器——错误的选择导致糟糕的性能
现在已经是2014年了,但是对大多数开发人员而言有两件事情仍然是个谜——垃圾回收以及异性(码农又被嘲笑了)。由于我对后者也不是特别了解,我想我还是试着说说前者吧,尤其是随着Java 8的到来,这个领域也发生了许多重大的变化及提升,其中最重要的莫过于持久代(PermGen)的删除以及一些令人振奋的新的优化(后面会陆续提及这些)。
说起垃圾回收,许多人都了解它的概念,也在日常的编程中有所应用。尽管如此,仍有许多我们不太了解的东西,而这正是痛苦的根源。关于JVM最大的误解就是认为它只有一个垃圾回收器,而事实上它有四个不同的回收器,每个都各有其长短。JVM并不会自动地选择某一个,这事还得落在你我的肩上,因为不同的回收器会带来吞吐量及应用的暂停时间的显著的差异。
这四种回收算法的共同之处在于它们都是分代的,也就是说它们将托管的堆分成了好几个区域,它假设堆中的许多对象的生命周期都很短,可以很快被回收掉。介绍这块内容的已经很多了,因此这里我打算直接讲一下这几个不同的算法,以及它们的长处及短处。
1. 串行回收器
串行回收器是最简单的一个,你都不会考虑使用它,因为它主要是面向单线程环境的(比如说32位的或者Windows)以及比较小的堆。这个回收器工作的时候会将所有应用线程全部冻结,就这一点而言就使得它完全不可能会被服务端应用所采用。
如何使用它:你可以打开-XX:+UseSerialGC这个JVM参数来使用它。
2. 并行/吞吐量回收器
下一个是并行回收器( Parallel collector)。这是JVM的默认回收器。正如它的名字所说的那样,它的最大的优点就是它使用多个线程来扫描及压缩堆。它的缺点就是不管执行的是minor GC还是full GC它都会暂停应用线程。并行回收器最适合那些可以容许暂停的应用,它试图减少由回收器所引起的CPU开销。
3. CMS回收器
并行回收器之后就是CMS回收器了(concurrent-mark-sweep)。这个算法使用了多个线程(concurrent)来扫描堆并标记(mark)那些不再使用的可以回收(sweep)的对象。这个算法在两种情况下会进入一个”stop the world”的模式:当进行根对象的初始标记的时候 (老生代中线程入口点或静态变量可达的那些对象)以及当这个算法在并发运行的时候应用程序改变了堆的状态使得它不得不回去再次确认自己标记的对象都是正确的。
使用这个回收器最大的问题就是会碰到promotion failure,这是指在回收新生代及年老代时出现了竞争条件的情况。如果回收器需要将年轻的对象提升到年老代中,而这个时候年老代没有多余的空间了,它就只能先进行一次STW(Stop The World)的full GC了——这种情况正是CMS所希望避免的。为了确保这种情况不会发生,你要么就是增加老生代的大小(或者增加整个堆的大小),要么就是给回收器分配一些后台线程以便与对象分配的速度进行赛跑。
这个算法的另一个缺点就是和并行回收器相比,它使用的CPU资源会更多,它使用了多个线程来执行扫描和回收,这样才能让应用持续提供更高级别的吞吐量。对于大多数长期运行的程序而言,应用的暂停对它们是很不利的,这个时候可以考虑使用CMS回收器。尽管如此,这个算法也不是默认开启的。你得指定XX:+UseConcMarkSweepGC来启用它。假设你的堆小于4G,而你又希望分配更多的CPU资源以避免应用暂停,那么这就是你要选择的回收器。然而,如果堆大于4G的话,你可能更希望使用最后的这个——G1回收器。
4. G1回收器
G1( Garbage first)回收器在JDK 7update 4中首次引入,它的设计目标是能更好地支持大于4GB的堆。G1回收器将堆分为多个区域,大小从1MB到32MB不等,并使用多个后台线程来扫描它们。G1回收器会优先扫描那些包含垃圾最多的区域,这正是它的名字的由来(Garbage first)。这个回收器可以通过-XX:UseG1GC标记来启用。
这一策略减少了后台线程还未扫描完无用对象前堆就已经用光的可能性,而那种情况回收器就必须得暂停应用,这就会导致STW回收。G1的另一个好处就是它总是会进行堆的压缩,而CMS回收器只有在full GC的时候才会干这事。
过去几年里,大堆一直都是一个充满争议的领域,很多开发人员从单机器单JVM模型转向了单机器多JVM的微服务,组件化的架构。这是许多因素所驱动的,包括隔离程序的组件,简化部署,避免重新加载应用类到内存所产生的开销(Java 8中这点已经得到了改善)。
尽管如此,这么做最主要还是希望能避免大堆的GC中长时期的”stop the world”的暂停(在一次大的回收中需要花费数秒才能完成)。像Docker这样的容器技术也加速了这一进程,它们使得你可以很轻松地在同一台物理机上部署多个应用。
Java 8及G1回收器
Java 8 update 20所引入的一个很棒的优化就是G1回收器中的字符串去重(String deduplication)。由于字符串(包括它们内部的char[]数组)占用了大多数的堆空间,这项新的优化旨在使得G1回收器能识别出堆中那些重复出现的字符串并将它们指向同一个内部的char[]数组,以避免同一个字符串的多份拷贝,那样堆的使用效率会变得很低。你可以使用-XX:+UseStringDeduplication这个JVM参数来试一下这个特性。
Java 8及持久代
Java 8中最大的改变就是持久代的移除,它原本是用来给类元数据,驻留字符串,静态变量来分配空间的。这在以前都是需要开发人员来针对那些会加载大量类的应用来专门进行堆比例的优化及调整。许多年来都是如此,这也正是许多OutOfMemory异常的根源,因此由JVM来接管它真是再好不过了。即便如此,它本身并不会减少开发人员将应用解耦到不同的JVM中的可能性。
每个回收器都有许多不同的开关和选项来进行调优,这可能会增加吞吐量,也可能会减少,这取决于你的应用的具体的行为了。在下一篇文章中我们会深入讲解配置这些算法的关键策略。
英文原文链接
分享到:
相关推荐
- **空闲时间**:在系统空闲时,垃圾回收器会选择性地进行垃圾回收。 - **内存紧张**:当系统内存不足时,垃圾回收器会被强制启动。 #### 六、垃圾回收的方法调用 虽然Java中的垃圾回收是由虚拟机自动完成的,但在...
本文将详细介绍Java中的垃圾回收机制及其工作原理,并探讨JVM如何管理和优化垃圾回收过程。 #### 二、JVM内存模型 JVM内存模型主要包括永久代(Permanent Generation, PermGen)、堆(Heap)和栈(Stack)三大部分。值得...
垃圾回收机制会根据不同的代采用不同的垃圾回收策略。 Java垃圾回收机制是Java虚拟机中的一种机制,用于防止内存泄露和有效地使用空闲的内存。该机制的算法有多种,每种算法都有其优缺点,选择合适的算法取决于具体...
当对象的引用计数变为0时,表示没有变量再使用这个对象,垃圾回收器就会回收这块内存。但这种机制对循环引用(两个或更多对象互相引用)处理不足,这时就需要其他策略介入。 接下来是标记清除(Mark and Sweep)...
然而,为了在C++中实现类似的功能,我们可以构建自定义的垃圾回收器。本话题将深入探讨如何用C++实现一个简单的单线程垃圾回收器。 首先,理解垃圾回收的基本原理至关重要。垃圾回收器的主要任务是识别那些已经不再...
Java和C#都采用了类似的分代垃圾回收策略,但具体实现细节有所不同。Java的垃圾回收更加细致地划分了内存区域,提供了更多的配置选项,以满足不同场景的需求;而C#则更注重于简化配置,通过默认设置来提供较好的性能...
【C#垃圾回收机制GC】深入解析 垃圾回收(Garbage Collection, GC)是现代编程语言中用于自动管理内存的一种机制。它的核心思想是通过跟踪和回收那些不再被程序引用的对象,以避免内存泄漏和提高内存利用率。在.NET...
Java垃圾回收机制(GC)是Java编程语言的关键特性,它自动管理内存,释放不再使用的对象,以防止内存泄漏。GC的运作方式主要有两种策略:引用计数和对象引用遍历。 引用计数是一种简单但不完美的方法。每个对象都有...
.NET 垃圾回收机制的原理是基于“标志紧缩”(Mark and Compact)算法的,它可以将内存资源分配到不同的代(Generation)中,并根据对象的生命周期来决定是否回收对象。.NET 垃圾回收机制支持三代对象:第一代对象是...
在"java垃圾回收机制介绍.doc"文档中,可能还会深入讨论如何监控和调试垃圾回收,包括使用JConsole、VisualVM等工具,以及分析GC日志,以理解垃圾回收的性能和行为,从而优化应用程序的内存使用。 了解和掌握Java...
除了自动垃圾回收之外,Lua还提供了一系列垃圾收集器函数,使开发者能够在必要时手动干预垃圾回收过程。 - `collectgarbage("collect")`:执行完整的垃圾回收周期。 - `collectgarbage("count")`:返回当前程序占用...
在Java中,对象的生命周期是由垃圾回收器来管理的,它自动释放不再被程序引用的对象,从而避免了内存泄漏等问题。 #### 三、垃圾回收的意义 在传统语言如C++中,程序员需要手动管理对象的内存,这意味着对象所占的...
本章主要介绍了垃圾回收算法和JVM中实现的不同类型的垃圾回收器。 首先,垃圾回收的目的是识别并回收不再使用的对象,释放它们占用的内存资源。在Java中,主要采用可达性分析法来确定哪些对象是可回收的,即从根...
- 分代收集算法:这是现代JVM垃圾回收器普遍采用的一种方法,它结合了上述几种算法。新创建的对象通常放在称为“新生代”的内存区域,经过多次垃圾回收仍然存活的对象则会被移动到“老年代”。新生代采用复制算法,...
在Java中,垃圾回收器(Garbage Collector)主要关注对象的生命周期。当一个对象不再有任何引用指向它时,就认为该对象是"垃圾",可以被回收。GC采用分代收集算法,将内存分为新生代(Young Generation)、老年代...
Java虚拟机(JVM)内存模型和垃圾回收机制是Java开发中至关重要的概念。本文将深入探讨这两个主题,帮助理解JVM如何管理...开发者应根据实际需求选择合适的垃圾回收器,并关注内存分配策略,以实现高效稳定的程序运行。
Java垃圾回收器(Garbage Collector, GC)是Java编程语言中的一个重要特性,它负责自动管理内存,自动回收不再使用的对象,以防止内存泄漏。在Java中,程序员无需手动释放内存,这一过程由JVM(Java虚拟机)自动完成...