1、如何判断对象已死
1.1、引用计数法
给对象添加一个引用计数器,每当有一个地方引用它时,计数器就加1;当引用失效时,计数器就减1;任何时刻计数器为0的对象就是不可能再被使用的。这种方法实现简单,判定效率高,但是很难解决对象之间互相循环引用的问题。
1.2、可达性分析法
通过一系列称为“GC Roots”的对象作为起点,从这些节点来时向下搜索,搜索所走过的路径称为引用链,当一个对象到GCRoots没有任何引用链相连时,则证明此对象是不可用的。
可以作为GCRoots的对象:
虚拟机栈中引用的对象;
方法区中静态属性引用的对象;
方法区中常量引用的对象;
本地方法栈中JNI引用的对象。
2、垃圾收集算法 要宣告一个对象的死亡,至少需要经历两次标记过程:如果对象在进行可达性分析后发现没有雨GCRoots相连接的引用链,那它将被第一次标记并且进行一次筛选,筛选条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法,或者该方法已经被调用过,虚拟机将这两种情况都视为没有必要执行。
如果被判定为有必要执行finalize()方法,那么这个对象将会被防止在一个叫做F-Queue的队列中,并在稍后由一个由虚拟机自动建立的、低优先级的Finalizer线程去执行它。这里所谓的执行是虚拟机会触发这个方法,但是不承诺会等待它运行结束(这样做是为了防止一个对象在finalize()方法中执行缓慢,或者发生了死循环,从而导致F-Queue队列中其他对象永远处于等待,甚至整个内存回收系统崩溃)。finalize()方法是对象最后逃脱死亡的机会,稍后GC将对F-Queue中的对象进行第二次标记,在第二次被标记时对象将被移出队列,那就基本上真的被回收了。如果要在finalize()方法中自救,只要重新与引用链上任何一个对象建立关联即可,譬如将自己赋值给某个变量。
2.1、标记-清除算法
算法分为标记和清除两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象,标记过程就是前面介绍过的。缺陷:标记和清楚的两个过程的效率都不高;另一个是清除过后会产生很多的不连续的内存碎片,碎片过多会导致需要分配大内存对象时,无法找到足够的连续内存而不得不提前触发另一次的垃圾收集动作。
2.2、复制算法
为了解决效率的问题,复制算法出现了,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当一块内存用完了,就将还存活的对象复制到另一块,然后再把自己使用过的内存一次清理掉。实现简单,运行高效。缺点就是可使用内存缩小为原来的一半,代价未必高了点。
2.3、标记-整理算法
复制算法在对象存活率较高时就要进行较多的复制操作,效率将会贬低,所以老年代一般不能直接选用这种算法。
根据老年代的特点,提出了标记-整理算法,该算法标记过程与标记-清楚算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活对象都向一段移动,然后直接清理掉端边界以外的内存。
2.4、分代收集算法 JVM一般都是把java堆分为新生代和老年代,根据各个年代的特点采用最合适的收集算法。一般新生代使用复制算法,老年代使用标记-清理或者标记-整理算法。
3、垃圾收集器
3.1、Serial
这是一个单线程的收集器,但它的“单线程”的意义并不仅仅说明它只会使用一个CPU或一条手机线程去完成垃圾手机工作,更重要的是在它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束。“Stop the world”,由虚拟机在后台自动发起和自动完成的,在用户不可见的情况下把用户正常工作的线程全部停掉,这对很多应用来说都是难以接受的。它是虚拟机运行在Client模式下的默认新生代收集器。
优点:简单而高效(与其他收集器的单线程比),对于限定单个CPU的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。
3.2、ParNew
ParNew收集器其实就是serial收集器的多线程版本,除了使用多条线程进行垃圾收集之外,其余行为与Serial收集器一样。ParNew收集器也是使用-XX:+UseConcMarkSweepGC选项后的默认新生代收集器,也可以使用-XX:+UseParNewGC选项来强制指定它。
3.3、Parallel Scavenge
Parallel Scavenge收集器也是一个新生代收集器,它也是使用复制算法的收集器,又是并行多线程收集器。parallel Scavenge收集器的特点是它的关注点与其他收集器不同,CMS等收集器的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间,而parallel Scavenge收集器的目标则是达到一个可控制的吞吐量。吞吐量= 程序运行时间/(程序运行时间 + 垃圾收集时间),虚拟机总共运行了100分钟。其中垃圾收集花掉1分钟,那吞吐量就是99%。
Parallel Scavenge收集器提供了两个参数用于精确控制吞吐量,分别是控制最大垃圾收集停顿时间的-XX:MaxGCPauseMillis参数以及直接设置吞吐量大小的-XX:GCTimeRatio参数。
3.4、CMS
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。CMS收集器是基于“标记-清除”算法实现的,整个收集过程大致分为4个步骤:
- 初始标记(CMS initial mark)
- 并发标记(CMS concurrenr mark)
- 重新标记(CMS remark)
- 并发清除(CMS concurrent sweep)
其中初始标记、重新标记这两个步骤任然需要停顿其他用户线程。初始标记仅仅只是标记出GC ROOTS能直接关联到的对象,速度很快,并发标记阶段是进行GC ROOTS 根搜索算法阶段,会判定对象是否存活。而重新标记阶段则是为了修正并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间会被初始标记阶段稍长,但比并发标记阶段要短。
由于整个过程中耗时最长的并发标记和并发清除过程中,收集器线程都可以与用户线程一起工作,所以整体来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。
CMS收集器的优点:并发收集、低停顿,但是CMS还远远达不到完美,器主要有三个显著缺点:
CMS收集器对CPU资源非常敏感。在并发阶段,虽然不会导致用户线程停顿,但是会占用CPU资源而导致引用程序变慢,总吞吐量下降。CMS默认启动的回收线程数是:(CPU数量+3) / 4。
CMS收集器无法处理浮动垃圾,可能出现“Concurrent Mode Failure“,失败后而导致另一次Full GC的产生。由于CMS并发清理阶段用户线程还在运行,伴随程序的运行自热会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS无法在本次收集中处理它们,只好留待下一次GC时将其清理掉。这一部分垃圾称为“浮动垃圾”。也是由于在垃圾收集阶段用户线程还需要运行,即需要预留足够的内存空间给用户线程使用,因此CMS收集器不能像其他收集器那样等到老年代几乎完全被填满了再进行收集,需要预留一部分内存空间提供并发收集时的程序运作使用。在默认设置下,CMS收集器在老年代使用了68%的空间时就会被激活,也可以通过参数-XX:CMSInitiatingOccupancyFraction的值来提供触发百分比,以降低内存回收次数提高性能。要是CMS运行期间预留的内存无法满足程序其他线程需要,就会出现“Concurrent Mode Failure”失败,这时候虚拟机将启动后备预案:临时启用Serial Old收集器来重新进行老年代的垃圾收集,这样停顿时间就很长了。所以说参数-XX:CMSInitiatingOccupancyFraction设置的过高将会很容易导致“Concurrent Mode Failure”失败,性能反而降低。
最后一个缺点,CMS是基于“标记-清除”算法实现的收集器,使用“标记-清除”算法收集后,会产生大量碎片。空间碎片太多时,将会给对象分配带来很多麻烦,比如说大对象,内存空间找不到连续的空间来分配不得不提前触发一次Full GC。为了解决这个问题,CMS收集器提供了一个-XX:UseCMSCompactAtFullCollection开关参数,用于在Full GC之后增加一个碎片整理过程,还可通过-XX:CMSFullGCBeforeCompaction参数设置执行多少次不压缩的Full GC之后,跟着来一次碎片整理过程。
3.5、Serial Old(MSC)
Serial Old是Serial收集器的老年代版本,它同样使用一个单线程执行收集,使用“标记-整理”算法。主要使用在Client模式下的虚拟机。
3.6、Parallel Old
Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。
3.7、G1
G1是一款面向服务器应用垃圾收集器,与其他GC收集器想必,G1具备以下特点:
- 并行与并发:G1能充分利用多CPU、多核环境下的硬件优势,使用多个CPU来缩短Stop-The-World停顿的时间,部分其他收集器原本需要停顿Java线程执行的GC动作,G1收集器仍然可以通过并发的方式让Java程序继续执行。
- 分代收集:与其他收集器一样,分代概念在G1中依然得以保留。虽然G1可以不要其他收集器配合就能独立管理整个GC堆,但它能够采用不同的方式去处理新创建的对象和已经存活了一半时间、熬过多次GC的旧对象以获取更好的收集效果。
- 空间整合:与CMS的“标记-清理”算法不同,G1从整体上看是基于“标记-整理”算法实现的收集器,从局部(两个Region之间)上来看是基于“复制”算法实现,无论如何,这两种算法都意味着G1运行期间不会产生内存空间碎片,收集后能提供规整的可用内存。
- 可预测的停顿:这是G1相对于CMS的另一个大优势,降低停顿时间是G1和CMS共同的关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,小号在垃圾收集上的时间不能超过N毫秒,这几乎已经是实时Java(RTSJ)的垃圾收集器的特征了。
在G1收集器中,Region之间的对象引用以及其他收集器中的新生代与老年代之间的对象引用,虚拟机都是使用Remembered Set来避免全堆扫描的。G1中每个Region都有一个与之对应的Remebered Set,虚拟机发现程序在对Reference类型的数据进行写操作时,会产生一个Write Barrier暂时中断写操作,检查Reference引用的对象是否处于不同的Region之中(在分代的例子中,就是检查是否老年代中的读写引用了新生代中的对象)。如果是,便通过CardTable把相关引用信息记录到被引用对象所属的Region的Remembered Set之中。当进行内存回收时,在GC根节点的枚举范围中加入Rememered Set即可保证不对全队扫描也不会有遗漏。
如果不计算维护Remembered Set的操作,G1收集器的运作大致可划分为以下几个步骤:
- 初始标记
- 并发标记
- 最终标记
- 筛选标记
垃圾收集参数总结
-XX:+UseSerialGC | Jvm运行在Client模式下的默认值,打开此开关后,使用Serial + Serial Old的收集器组合进行内存回收 |
-XX:+UseParNewGC | 打开此开关后,使用ParNew + Serial Old的收集器进行垃圾回收 |
-XX:+UseConcMarkSweepGC | 使用ParNew + CMS + Serial Old的收集器组合进行内存回收,Serial Old作为CMS出现“Concurrent Mode Failure”失败后的后备收集器使用。 |
-XX:+UseParallelGC | Jvm运行在Server模式下的默认值,打开此开关后,使用Parallel Scavenge + Serial Old的收集器组合进行回收 |
-XX:+UseParallelOldGC | 使用Parallel Scavenge + Parallel Old的收集器组合进行回收 |
-XX:SurvivorRatio | 新生代中Eden区域与Survivor区域的容量比值,默认为8,代表Eden:Subrvivor = 8:1 |
-XX:PretenureSizeThreshold | 直接晋升到老年代对象的大小,设置这个参数后,大于这个参数的对象将直接在老年代分配 |
-XX:MaxTenuringThreshold | 晋升到老年代的对象年龄,每次Minor GC之后,年龄就加1,当超过这个参数的值时进入老年代 |
-XX:UseAdaptiveSizePolicy | 动态调整java堆中各个区域的大小以及进入老年代的年龄 |
-XX:+HandlePromotionFailure | 是否允许新生代收集担保,进行一次minor gc后, 另一块Survivor空间不足时,将直接会在老年代中保留 |
-XX:ParallelGCThreads | 设置并行GC进行内存回收的线程数 |
-XX:GCTimeRatio GC | 时间占总时间的比列,默认值为99,即允许1%的GC时间,仅在使用Parallel Scavenge 收集器时有效 |
-XX:MaxGCPauseMillis | 设置GC的最大停顿时间,在Parallel Scavenge 收集器下有效 |
-XX:CMSInitiatingOccupancyFraction | 设置CMS收集器在老年代空间被使用多少后出发垃圾收集,默认值为68%,仅在CMS收集器时有效,-XX:CMSInitiatingOccupancyFraction=70 |
-XX:+UseCMSCompactAtFullCollection | 由于CMS收集器会产生碎片,此参数设置在垃圾收集器后是否需要一次内存碎片整理过程,仅在CMS收集器时有效 |
-XX:+CMSFullGCBeforeCompaction | 设置CMS收集器在进行若干次垃圾收集后再进行一次内存碎片整理过程,通常与UseCMSCompactAtFullCollection参数一起使用 |
-XX:+UseFastAccessorMethods | 原始类型优化 |
-XX:+DisableExplicitGC | 是否关闭手动System.gc |
-XX:+CMSParallelRemarkEnabled | 降低标记停顿 |
-XX:LargePageSizeInBytes | 内存页的大小不可设置过大,会影响Perm的大小,-XX:LargePageSizeInBytes=128m |
Client、Server模式默认GC
Client | Serial 串行GC | Serial Old 串行GC |
Server | Parallel Scavenge | 并行回收GC Parallel Old 并行GC |
Sun/oracle JDK GC组合方式
-XX:+UseSerialGC | Serial 串行GC | Serial Old 串行GC |
-XX:+UseParallelGC | Parallel Scavenge 并行回收GC | Serial Old 并行GC |
-XX:+UseConcMarkSweepGC | ParNew 并行GC | CMS 并发GC 当出现“Concurrent Mode Failure”时,采用Serial Old 串行GC |
-XX:+UseParNewGC | ParNew 并行GC | Serial Old 串行GC |
-XX:+UseParallelOldGC | Parallel Scavenge | 并行回收GC Parallel Old 并行GC |
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC | Serial 串行GC | CMS 并发GC 当出现“Concurrent Mode Failure”时采用Serial Old 串行GC |
相关推荐
主要整理内容为:分析了垃圾收集的算法和JDK1.7中提供的7款垃圾收集器的特点以及运作原理。以及内存分配策略
了解和掌握Java的垃圾收集器与内存分配策略对于开发高性能、稳定的应用至关重要,这涉及到程序的运行效率、内存消耗和避免内存泄漏等问题。通过理解这些概念,开发者可以更好地理解和解决Java应用程序中的内存问题,...
JVM内存管理是Java虚拟机的核心机制之一,其主要包含对象的创建、内存分配、...通过对内存分配策略、对象生死判定、垃圾收集算法和垃圾收集器的理解与应用,可以更好地掌握JVM的内存管理,从而提升应用性能和稳定性。
《垃圾回收器与内存分配策略详解》 在Java编程中,理解垃圾回收(Garbage Collection,简称GC)机制和内存分配策略是至关重要的。GC的主要目的是自动管理内存,避免程序员手动进行繁琐且容易出错的内存释放工作。而...
内存分配与回收策略, JVM 调优, 文件结构, 类加载机制, Java 程序 Java是一种面向对象的编程语言,由Sun Microsystems于1995年推出。它是一种跨平台的语言,意味着可以在不同的操作系统上运行。Java具有简单、...
Java垃圾收集器与内存分配策略是Java性能优化的重要组成部分。垃圾收集器的主要任务是自动管理Java应用程序的内存,确保程序运行过程中有效地回收不再使用的对象,从而避免内存泄漏。本文将详细讲解Java垃圾收集器的...
《垃圾回收器与内存分配策略详解》 在Java编程中,理解垃圾回收(Garbage Collection,简称GC)机制和内存分配策略是至关重要的。GC的主要目的是自动管理内存,避免程序员手动进行繁琐且容易出错的内存释放工作。而...
1. 分配策略:操作系统采用不同的分配策略,如首次适应、最佳适应、最差适应等,以优化内存使用。这些策略决定了如何将空闲内存块分配给请求内存的进程。 2. 分页与分段:分页是将内存划分为固定大小的页,而分段则...
Java虚拟机(JVM)内存模型是Java程序运行的基础,它包括了多个关键的内存区域。...开发者需要根据应用特点调整内存参数,选择合适的垃圾收集策略,以避免内存溢出、提高系统效率并确保程序的稳定运行。
9. **Java虚拟机参数调整**:开发者可以通过JVM参数来调整内存分配策略,例如-Xms和-Xmx设置堆内存的初始大小和最大大小,-XX:NewRatio设置新生代和老年代的比例等。 10. **JVM内存诊断工具**:JVisualVM、jmap、...
本文将深入探讨Android中的内存分配策略及其在实际操作中的应用。 首先,我们需要了解内存分配的基本概念。内存分配是指在计算机运行过程中,操作系统为进程或线程分配所需的内存空间。在Android系统中,这个过程由...
在深入理解JVM内存模型与垃圾收集策略之前,我们首先需要知道JVM的主要组成部分:类装载器、运行数据区、执行引擎、本地方法接口和本地方法库。 1. **JVM内存模型** JVM内存主要分为以下几个区域: - **程序...
- **内存分配策略**:合理设置堆大小和各代的大小,避免内存溢出或频繁的垃圾收集。 - **引用类型**:了解软引用、弱引用、虚引用等不同类型的引用,它们可以帮助控制对象的生命周期,协助垃圾收集。 - **并行...
本篇将深入探讨两种重要的垃圾回收器——G1收集器和ZGC,以及Stop the World现象和内存分配策略。 首先,G1(Garbage-First)收集器是一种并行并发的垃圾回收器,旨在减少垃圾回收停顿时间。其特点是采用了区域...
1. 首次适应法(First Fit):这是最简单的内存分配策略,操作系统维护一个空闲块链表,当有新的内存请求时,它会遍历整个链表,找到第一个足够大的空闲块分配给请求者。这种方法简单,但可能导致内存碎片,因为大块...
在IBM JDK 5中,垃圾收集(Garbage Collection, GC)是Java平台中至关重要的一个部分,用于自动管理内存,确保程序高效运行。垃圾收集策略的选择直接影响到应用程序的性能,特别是对于WebSphere Application Server...
Java内存分配与回收策略 Java中的内存自动管理主要分为两种:内存的分配和内存的回收。在Java中,对象的内存分配主要是在堆上分配, Objects are primarily allocated in the Eden zone of the new generation. ...
轮转法(Round Robin)是一种简单的内存分配策略,常见于多进程或线程调度中。在Java中,虽然JVM并不直接支持轮转法进行内存分配,但可以通过模拟实现。例如,我们可以创建一个固定大小的内存池,然后按照预定义的...