jvm垃圾回收器工作原理及使用实例介绍-java开发java经验技巧

jvm垃圾回收器工作原理及使用实例介绍-java开发java经验技巧

ID:30769255

大小:199.95 KB

页数:15页

时间:2019-01-03

上传者:U-991
jvm垃圾回收器工作原理及使用实例介绍-java开发java经验技巧_第1页
jvm垃圾回收器工作原理及使用实例介绍-java开发java经验技巧_第2页
jvm垃圾回收器工作原理及使用实例介绍-java开发java经验技巧_第3页
jvm垃圾回收器工作原理及使用实例介绍-java开发java经验技巧_第4页
jvm垃圾回收器工作原理及使用实例介绍-java开发java经验技巧_第5页
资源描述:

《jvm垃圾回收器工作原理及使用实例介绍-java开发java经验技巧》由会员上传分享,免费在线阅读,更多相关内容在工程资料-天天文库

JVM垃圾回收器工作原理及使用实例介绍-编程开发技术JVM垃圾回收器工作原理及使用实例介绍原文出处:周明耀垃圾收集基础Java语言的一大特点就是可以进行自动垃圾回收处理,而无需开发人员过于关注系统资源,例如内存资源的释放情况。自动垃圾收集虽然大大减轻了开发人员的工作量,但是也增加了软件系统的负担。拥冇垃圾收集器可以说是Java语言与C++语言的一项显著区别。在C++语言中,程序员必须小心谨慎地处理每一项内存分配,且内存使用完后必须手工释放曾经占用的内存空间。当内存释放不够完全吋,即存在分配但永不释放的内存块,就会引起内存泄漏,严重时甚至导致程序瘫痪。以卜•列举了垃圾回收器常用的算法及实验原理:•引用计数法(ReferenceCounting)引用计数器在微软的COM组件技术中、Adobe的ActionScript3种都有使用。引用计数器的实现很简单,对于一个对象A,只要有任何一个对象引用了A,则A的引用计数器就加1,当引用失效时,引用计数器就减lo只要对象A的引用计数器的值为0,则对象A就不可能再被使用。引用计数器的实现也非常简单,只需要为每个对象配置一个整形的计数器即可。但是引用计数器有一个严重的问题,即无法处理循环引用的情况。因此,在Java的垃圾回收器中没有使用这种算法。一个简单的循环引用问题描述如下:有对象A和对象B,对象A中含冇对象B的引用,对象B中含有对象A的引用。此时,对象A和对象B的引用计数器都不为0o但是在系统屮却不存在任何第3个对彖引用了A或B。也就是说,A和B是应该被冋收的垃圾对彖,但由于垃圾对彖间相互引用,从而使垃圾冋收器无法识别,引起内存泄漏。•标记■清除算法(Mark-Sweep)标记-清除算法将垃圾回收分为两个阶段:标记阶段和清除阶段。一种可行的实现是,在标记阶段首先通过根节点,标记所有从根节点开始的较大对象。因此,未被标记的对象就是未被引用的垃圾对象。然后,在清除阶段,清除所有未被标 记的对象。该算法最大的问题是存在大量的空间碎片,因为回收后的空间是不连续的。在对象的堆空间分配过程中,尤其是大对象的内存分配,不连续的内存空间的工作效率要低于连续的空间。•复制算法(Copying)将现有的内存空间分为两快,每次只使用其中一块,在垃圾回收时将正在使用的内存中的存活对象复制到未被使用的内存块中,Z后,清除正在使用的内存块中的所冇对彖,交换两个内存的角色,完成垃圾回收。如果系统中的垃圾对彖很多,复制算法需要复制的存活对象数量并不会太大。因此在真正需要垃圾回收的吋刻,复制算法的效率是很高的。又由于对象在垃圾冋收过程中统一被复制到新的内存空间中,因此,可确保回收后的内存空间是没冇碎片的。该算法的缺点是将系统内存折半。Java的新生代串行垃圾回收器中使用了复制算法的思想。新生代分为eden空间、from空间、to空间3个部分。其中from空间和to空间可以视为用于复制的两块大小相同、地位相等,且可进行角色互换的空间块。from和to空间也称为survivor空间,即幸存者空间,用于存放未被冋收的对象。在垃圾回收时,cdcn空间中的存活对象会被复制到未使用的survivor空间中(假设是to),正在使用的survivor空间(假设是from)屮的年轻对彖也会被复制到to空间中(大对彖,或者老年对象会直接进入老年带,如果to空间己满,则对象也会直接进入老年代)。此时,eden空间和from空间屮的剩余对象就是垃圾对象,可以直接清空,to空间则存放此次回收后的存活对象。这种改进的复制算法既保证了空间的连续性,又避免了大量的内存空间浪费。•标记-压缩算法(Mark-Compact)复制算法的高效性是建立在存活对象少、垃圾对象多的前提下的。这种情况在年轻代经常发生,但是在老年代更常见的情况是大部分对象都是存活对象。如果依然使用复制算法,由于存活的对象较多,复制的成本也将很高。标记-压缩算法是一种老年代的回收算法,它在标记-清除算法的基础上做了一些优化。也首先需要从根节点开始对所有可达对彖做一次标记,但Z后,它并不简单地清理未标记的对彖,而是将所有的存活对象压缩到内存的一端。之后,清理边界外所有的空间。这种方法既避免了碎片的产生,又不需要两块相同的内存空间,因此,其性价比比较高。•增量算法(IncrementalCollecting)在垃圾回收过程中,应用软件将处于一种CPU消耗很高的状态。在这种CPU消耗很高的状态下,应用程序所有的线程都会挂起,暂停一切止常的工作,等待垃圾回收的完成。如果垃圾回收时间过长,应用程序会被挂起很久,将严重影响用户体验或者系统的稳定性。 增量算法的基本思想是,如果一次性将所冇的垃圾进行处理,需耍造成系统长时间的停顿,那么就可以让垃圾收集线程和应用程序线程交替执行。毎次,垃圾收集线程只收集一小片区域的内存空间,接着切换到应用程序线程。依次反复,直到垃圾收集完成。使用这种方式,由于在垃圾回收过程中,间断性地还执行了应用程序代码,所以能减少系统的停顿时间。但是,因为线程切换和上下文转换的消耗,会使得垃圾回收的总体成本上升,造成系统吞吐量的下降。•分代(GenerationalCollecting)根据垃圾冋收对彖的特性,不同阶段最优的方式是使用合适的算法用于本阶段的垃圾回收,分代算法即是基于这种思想,它将内存区间根据对象的特点分成几块,根据每块内存区间的特点,使用不同的回收算法,以提高垃圾回收的效率。以HotSpot虚拟机为例,它将所有的新建对象都放入称为年轻代的内存区域,年轻代的特点是对象会很快回收,因此,在年轻代就选择效率较高的复制算法。当一个对彖经过儿次冋收后依然存活,对象就会被放入称为老生代的内存空间。在老生代屮,几乎所有的对象都是经过几次垃圾冋收后依然得以幸存的。因此,可以认为这些对象在一段时期内,甚至在应用程序的整个生命周期中,将是常驻内存的。如果依然使用复制算法回收老生代,将需耍复制大量对象。再加上老生代的回收性价比也要低于新生代,因此这种做法也是不可取的。根据分代的思想,可以对老年代的冋收使用与新生代不同的标记-压缩算法,以提高垃圾冋收效率。从不同角度分析垃圾收集器,可以将其分为不同的类型。1.按线程数分,可以分为申行垃圾冋收器和并行垃圾冋收器。申行垃圾冋收器一次只使用一个线程进行垃圾回收;并行垃圾回收器一次将开启多个线程同时进行垃圾回收。在并行能力较强的CPU上,使用并行垃圾回收器可以缩短GC的停顿时间。2.按照工作模式分,可以分为并发式垃圾回收器和独占式垃圾回收器。并发式垃圾回收器与应用程序线程交替工作,以尽可能减少应用程序的停顿吋间;独占式垃圾回收器(Stopthewodd)—旦运行,就停止应用程序中的其他所有线程,直到垃圾回收过程完全结束。3.按碎片处理方式可分为压缩式垃圾冋收器和非压缩式垃圾冋收器。压缩式垃圾回收器会在回收完成后,对存活对象进行压缩整理,消除冋收后的碎片;非压缩式的垃圾回收器不进行这步操作。4.按工作的内存区间,又可分为新生代垃圾回收器和老年代垃圾回收器。可以用以下指标评价一个垃圾处理器的好坏。吞吐量:指在应用程序的生命周期内,应用程序所花费的时间和系统总运行时间的比值。系统总运行时间二应用程序耗时+GC耗时。如果系统运行了lOOmin,GC耗时lmin,那么系统的吞吐量就是(100-1)/100二99%。 垃圾回收器负载:和吞吐量相反,垃圾回收器负载指来记回收器耗时与系统运行总时间的比值。停顿时间:指垃圾回收器正在运行吋,应用程序的暂停时间。对于独占回收器而言,停顿时间可能会比较长。使用并发的冋收器时,由于垃圾冋收器和应用程序交替运行,程序的停顿时间会变短,但是,由于其效率很可能不如独占垃圾M收器,故系统的吞吐量可能会较低。垃圾冋收频率:指垃圾冋收器多长时间会运行一次。一般来说,对于固定的应用而言,垃圾冋收器的频率应该是越低越好。通常增大堆空间可以有效降低垃圾冋收发生的频率,但是可能会增加回收产生的停顿吋间。反应时间:指当一个对象被称为垃圾后多长吋间内,它所占据的内存空间会被释放。堆分配:不同的垃圾回收器对堆内存的分配方式可能是不同的。一个良好的垃圾收集器应该有一个合理的堆内存区间划分。JVM垃圾回收器分类•新生代串行收集器串行收集器主要有两个特点:第一,它仅仅使用单线程进行垃圾回收;第二,它独占式的垃圾回收。在串行收集器进行垃圾回收吋,Java应用程序小的线程都需要暂停,等待垃圾回收的完成,这样给用户体验造成较差效果。虽然如此,串行收集器却是一个成熟、经过长时间生产环境考验的极为高效的收集器。新生代吊行处理器使用复制算法,实现相对简单,逻辑处理特别高效,但没冇线程切换的开销。在诸如单CPU处理器或者较小的应用内存等硕件平台不是特别优越的场合,它的性能表现可以超过并行冋收器和并发冋收器。在HotSpot虚拟机屮,使用-XX:+UseSerialGC参数可以指定使用新生代串行收集器和老年代串行收集器。当JVM在Client模式卜•运行时,它是默认的垃圾收集器。一次新生代串行收集器的工作输出口志类似如清单1信息(使用-XX:+PrintGCDetails开关)所示。清单1.一次新生代串行收集器的工作输出日志[GC[DefNew:3468K->150K(9216K),0.0028638secs][Tenured:1562K->1712K(10240K),0.0084220secs]3468K-〉1712K(19456K),[Perm:377K->377K(12288K)],0.0113816secs][Times:user二0.02sys=0.00,real=0.01secs]它显示了一次垃圾回收前的新生代的内存占用量和垃圾回收后的新生代内存占用量,以及垃圾回收所消耗的吋间。 •老年代串行收集器老年代串行收集器使用的是标记-压缩算法。和新生代串行收集器一样,它也是一个串行的、独占式的垃圾冋收器。由于老年代垃圾冋收通常会使用比新生代垃圾回收更长的时间,因此,在堆空间较人的应用程序中,一旦老年代串行收集器启动,应用程序很可能会因此停顿几秒甚至更长时间。虽然如此,老年代串行冋收器可以和多种新生代回收器配合使用,同时它也可以作为CMS回收器的备用回收器。若要启用老年代串行回收器,可以尝试使用以下参S:-XX:+UseSerialGC:新生代、老年代都使用串行回收器。清单2.一次老年代串行收集器的工作输出日志Heapdefnewgenerationtotal4928K,used1373K[0x27010000,0x27560000,0x2c560000)edenspace4416K,31%used[0x27010000,0x2716762&0x27460000)fromspace512K,0%used[0x27460000,0x27460000,0x274e0000)tospace512K,0%used[0x274e0000,0x274e0000,0x27560000)tenuredgencrationtotal10944K,usedOK[0x2c560000,0x2d010000,0x37010000)thespace10944K,0%used0x2d010000)compactingpermgentotal0x3b010000)thespace12288K,3%used0x37cl0000)rospace10240K,51%used0x3bal0000)rwspace12288K,55%used0x3c610000)[0x2c560000,0x2c560000,0x2c560200,12288K,used376K[0x37010000,0x37cl0000,[0x37010000,[0x3b010000,[0x3bal0000,0x3706e0b8,0x3706e200,0x3b543000,0x3b543000,0x3c0ac4f8,0x3c0ac600,如果使用-XX:+UseParNewGC参数设置,表示新生代使用并行收集器,老年代使用串行收集器,如清单3所示。清单3.一次串并行收集器混合使用的工作输出日志Heapparnewgenerationtotal4928K,used1373K[OxOfOlOOOO,0x0f560000,0x14560000)edenspace4416K,31%used[OxOfOlOOOO,0x0fl67620,0x0f460000)fromspace512K,0%used[0x0f460000,0x0f460000,0x0f4e0000)tospace512K,0%used[0x0f4c0000,0x0f4c0000,0x0f560000)tenuredgenerationtotal10944K,usedOK[0x14560000,0x15010000,OxlfOlOOOO)thespace10944K,0%used[0x14560000,0x14560000,0x14560200,0x15010000)compactingpermgentotal12288K,used2056K[OxlfOlOOOO,OxlfclOOOO, 0x23010000)thespace12288K,16%used[OxlfOlOOOO,0xlf2121d0,0xlf212200,Oxlfc10000)Nosharedspacesconfigured.如果使用-XX:+UseParallelGC参数设置,表示新生代和老年代均使用并行回收收集器。如清单4所示。清单4.一次老年代并行回收器的工作输出日志[FullGC[Tenured:1712K->1699K(10240K),0.0071963secs]1712K->1699K(19456K),[Perm:377K->372K(12288K)],0.0072393secs][Times:user=0.00sys二0.00,real=0.01secs]它显示了垃圾回收前老年代和永久区的内存占用量,以及垃圾回收后老年代和永久区的内存使用量。•并行收集器并行收集器是工作在新牛代的垃圾收集器,它只简单-地将串行回收器多线程化。它的回收策略、算法以及参数和串行回收器一样。并行回收器也是独占式的回收器,在收集过程中,应用程序会全部暂停。但由于并行回收器使用多线程进行垃圾回收,因此,在并发能力比较强的CPU上,它产生的停顿时间要短于串行回收器,而在单CPU或者并发能力较弱的系统中,并行回收器的效果不会比串行回收器好,rtr丁多线程的压力,它的实际表现很可能比串行冋收器差。开启并行回收器可以使用参数-XX:+UscParNewGC,该参数设置新生代使用并行收集器,老年代使用串行收集器。清单5.设置参数-XX:+UseParNewGC的输出日志[GC[ParNew:825K->161K(4928K),0.0155258secs][Tenured:8704K->661K(10944K),0.0071964sees]9017K->661K(15872K),[Perm:2049K->2049K(12288K)],0.0228090secs][Times:user=0.01sys二0.00,real二0.01secs]Heapparnewgenerationtotal4992K,used179K[OxOfOlOOOO,0x0f570000,0x14560000)edenspace4480K,4%used[OxOfOlOOOO,0x0f03cda&0x0f470000)fromspace512K,0%used[0x0f470000,0x0f470000,0x0f4f0000)tospace512K,0%used[0x0f4f0000,0x0f4f0000,0x0f570000) tenuredgenerationtotal10944K,used8853K[0x14560000,0x15010000,OxlfOlOOOO)thespace10944K,80%used[0x14560000,0xl4e057c0,0xl4e05800,0x15010000)compactingpermgentotal12288K,used2060K[OxlfOlOOOO,OxlfclOOOO,0x23010000)thespace12288K,16%used[OxlfOlOOOO,0xlf21322&0xlf213400,OxlfclOOOO)Nosharedspacesconfigured.设置参数-XX:+UscConcMarkSwecpGC可以要求新生代使用并彳亍收集器,老年代使用CMSo清单6.设置参数-XX:+UseConcMarkSweepGC的输出日志[GC[ParNew:8967K->669K(14784K),0.0040895secs]8967K->669K(63936K),0.0043255secs][Times:user二0.00sys=0.00,real=0.00secs]Heapparnewgencrationtotal14784K,used9389K[0x03f50000,0x04f50000,0x04f50000)edenspace13184K,66%used[0x03f50000,0x047d3e5&0x04c30000)fromspace1600K,41%used[OxOddcOOOO,0x04e67738,0x04f50000)tospaceWOOK,0%used[0x04c30000,0x04c30000,0x04dc0000)concurrentmark-sweepgencreitiontotal49152K,usedOK[0x04f50000,0x07f50000,0x09f50000)concurrent-mark-sweeppermgentotal12288K,used2060K[0x09f50000,0x0ab50000,0x0df50000)并行收集器工作时的线程数量可以使用-XX:ParallelGCThreads参数指定。一般,最好与CPU数量相当,避免过多的线程数影响垃圾收集性能。在默认情况下,当CPU数量小于8个,ParallelGCThreads的值等于CPU数量,大于8个,ParallelGCThreads的值等于3+[5*CPU_Count]/8]。以卜测试显示了笔者笔记木上运行8个线程时耗时最短,木人笔狂木是8核TntelCPUo清单7.设置为8个线程后GC输出[GC[ParNew:8967K->676K(14784K),0.0036983secs]8967K->676K(63936K),0.0037662secs][Times:user=0.00sys=0.00,real=0.00secs]Heapparnewgenerationtotal14784K,used9395K[0x040e0000,0x050e0000,0x050e0000)edenspace13184K,66%used[OxOdOeOOOO,0x04963e5&OxOddcOOOO)fromspaceWOOK,42%used[0x04f50000,0x04ff9100,0x050e0000)tospaceWOOK,0%used[0x04dc0000,0x04dc0000,0x04f50000)concurrentmark-sweepgenerationtotal49152K,usedOK[0x050e0000,0x080e0000,OxOaOeOOOO) concurrent-mark-sweeppermgentotal12288K,used2060K[OxOaOeOOOO,OxOaceOOOO,OxOeOeOOOO)清单8.设置为128个线程后GC输出[GC[ParNew:8967K->664K(14784K),0.0207327secs]8967K->664K(63936K),0.0208077secs][Times:user=0.03sys=0.00,real=0.02secs]清单9.设置为640个线程后GC输出[GC[ParNew:8967K->667K(14784K),0.2323704secs]8967K->667K(63936K),0.2324778secs][Times:user=0.34sys=0.02,real=0.23secs]清单10.设置为1280个线程后GC输出ErroroccurredduringinitializationofVMToosmallnewsizespecified•新生代并行回收(ParallelScavenge)收集器新生代并行回收收集器也是使用复制算法的收集器。从表面上看,它和并行收集器一样都是多线程、独占式的收集器。但是,并行回收收集器有一个重耍的特点:它非常关注系统的吞吐量。新牛代并行冋收收集器可以使用以下参数启用:-XX:+UseParallelGC:新生代使用并行回收收集器,老年代使用串行收集器。-XX:+UseParallel01dGC:新生代和老年代都是用并行回收收集器。清单11.设置为24个线程后GC输出HeapPSYoungGentotal4800K,used893K[OxldacOOOO,OxleOlOOOO,0x23010000)edenspace4160K,21%used[OxldacOOOO,0xldb9f570,OxldedOOOO)fromspace640K,0%used[0xldf70000,0xldf70000,OxleOlOOOO)tospace640K,0%used[OxldedOOOO,OxldedOOOO,0xldf70000)ParOldGentotal19200K,used16384K[0x13010000,0xl42d0000,OxldacOOOO)objectspace19200K,85%used[0x13010000,0x14010020,0xl42d0000)PSPermGentotal12288K,used2054K[OxOfOlOOOO,OxOfclOOOO,0x13010000)objectspace12288K,16%used[OxOfOlOOOO,0x0f2119c0,OxOfclOOOO)新生代并行冋收收集器可以使用以下参数启用:-XX:+MaxGCPauscMills:设置最大垃圾收集停顿时间,它的值是一个大于0的整 数。收集器在工作时会调整Java堆大小或者-其他一些参数,尽可能地把停顿时间控制在MaxGCPauseM订Is以内。如果希望减少停顿时间,而把这个值设置得很小,为了达到预期的停顿吋间,JVM可能会使用一个较小的堆(一个小堆比一个大堆回收快),而这将导致垃圾回收变得很频繁,从而增加了垃圾回收总时间,降低了吞叶量。-XX:+GCTimeRatio:设置吞吐量大小,它的值是一个0-100Z间的整数。假设GCTimeRatio的值为n,那么系统将花费不超过l/(l+n)的时间用于垃圾收集。比如GCTimeRatio等于19,则系统用于垃圾收集的时间不超过1/(1+19)二5%。默认情况下,它的取值是99,即不超过1%的时间用于垃圾收集。除此Z外,并行回收收集器与并行收集器另一个不同Z处在于,它支持-•种自适应的GC调节策略,使用-XX:+UseAdaptiveSizePolicy可以打开自适应GC策略。在这种模式下,新生代的大小、eden和survivor的比例、晋升老年代的对象年龄等参数会被自动调整,以达到在堆犬小、吞吐量和停顿吋间之间的平衡点。在手工调优比较困难的场合,可以宜接使用这种自适应的方式,仅指定虚拟机的最大堆、口标的吞吐量(GCTimeRatio)和停顿时间(MaxGCPauscM订Is),让虚拟机自己完成调优工作。清单12.新生代并行回收收集器GC输出HeapPSYoungGentotal4800K,used893K[OxldacOOOO,OxleOlOOOO,0x23010000)edenspace4160K,21%used[OxldacOOOO,0xldb9f570,OxldedOOOO)fromspace640K,0%used[Oxldf7OOOO,0xldf70000,OxlcOlOOOO)tospace640K,0%used[OxldedOOOO,OxldedOOOO,0xldf70000)PSOldGentotal19200K,used16384K[0x13010000,0xl42d0000,OxldacOOOO)objectspace19200K,85%used[0x13010000,0x14010020,0xl42d0000)PSPermGentotal12288K,used2054K[OxOfOlOOOO,OxOfclOOOO,0x13010000)objectspace12288K,16%used[OxOfOlOOOO,0x0f2119c0,OxOfclOOOO)它也显示了收集器的工作成果,也就是回收前的内存大小和回收后的内存大小,以及花费的时间。•老年代并行回收收集器老年代的并行回收收集器也是一种多线程并发的收集器。和新生代并行回收收集器一样,它也是一种关注吞吐量的收集器。老年代并行回收收集器使用标记-压缩算法,JDK1.6之后开始启用。使用-XX:+UseParallel01dGC可以在新生代和老生代都使用并行回收收集器,这是一对非常关注吞吐量的垃圾收集器组合,在对吞吐量皱感的系统中,叮以考虑使用。参数-XX:ParallclGCThrcads也可以用于设置垃圾回收时的线程数量。清单13是设置线程数量为100时老年代并行回收收集器输出日志:清单13.老年代并行回收收集器设置100线程时GC输出 HeapPSYoungGentotal4800K,used893K[OxldeicOOOO,OxleOlOOOO,0x23010000)edenspace4160K,21%used[OxldacOOOO,0xldb9f570,OxldedOOOO)fromspace640K,0%used[0xldf70000,0xldf70000,OxleOlOOOO)tospace640K,0%used[OxldedOOOO,OxldedOOOO,0xldf70000)ParOldGentotal19200K,used16384K[0x13010000,0xl42d0000,OxldacOOOO)objcctspace19200K,85%used[0x13010000,0x14010020,0xl42d0000)PSPermGentotal12288K,used2054K[OxOfOlOOOO,OxOfc10000,0x13010000)objectspace12288K,16%used[OxOfOlOOOO,0x0f2119c0,OxOfclOOOO)•CMS收集器与并行回收收集器不同,CMS收集器主要关注于系统停顿时间。CMS是ConcurrentMarkSweep的缩写,意为并发标记清除,从名称上可以得知,它使用的是标记-清除算法,同时它又是一个使用多线程并发回收的垃圾收集器。CMS工作时,主要步骤有:初始标记、并发标记、重新标记、并发清除和并发重置。其中初始标记和重新标记是独占系统资源的,而并发标记、并发清除和并发重置是可以和用户线程一起执行的。因此,从整体上来说,CMS收集不是独占式的,它可以在应用程序运行过程小进行垃圾回收。根据标记-清除算法,初始标记、并发标记和重新标记都是为了标记出需要回收的对彖。并发清理则是在标记完成后,正式冋收垃圾对象;并发重置是指在垃圾回收完成后,重新初始化CMS数据结构和数据,为下一次垃圾回收做好准备。并发标记、并发清理和并发重置都是可以和应用程序线程一起执行的。CMS收集器在其主要的工作阶段虽然没有暴力地彻底暂停应用程序线程,但是由于它和应用程序线程并发执行,相互抢占CPU,所以在CMS执行期内对应用程序吞吐量造成一定影响。CMS默认启动的线程数是(Paral1elGCThreads+3)/4),ParallelGCThreads是新牛代并行收集器的线程数,也可以通il-XX:ParallelCMSThreads参数手工设定CMS的线程数量。当CPU资源比较紧张时,受到CMS收集器线程的彩响,应用程序的性能在垃圾回收阶段可能会非常糟糕。由于CMS收集器不是独占式的回收器,在CMS回收过程中,应用程序仍然在不停地工作。在应用程序工作过程中,又会不断地产生垃圾。这些新生成的垃圾在当前CMS回收过程小是无法清除的。同时,因为应用程序没有中断,所以在CMS回收过程屮,还应该确保应用程序有足够的内存可用。因此,CMS收集器不会等待堆内存饱和时才进行垃圾回收,而是当前堆内存使用率达到某一阈值时,便开始进行回收,以确保应用程序在CMS工作过程中依然有足够的空间支持应用程序运行。这个回收阈值可以使用-XX:CMSInitiatingOccupancyFraction來指定,默认是68o即当老年代的空间使用率达到68%时,会执行一次CMS回收。如果应用程 序的内存使用率增长很快,在CMS的执行过程中,已经出现了内存不足的情况,此吋,CMS冋收将会失败,JVM将启动老年代串行收集器进行垃圾冋收。如果这样,应用程序将完全中断,直到垃圾收集完成,这时,应用程序的停顿时间可能很长。因此,根据应用程序的特点,口J以对-XX:CMSInitiatingOccupancyFraction进行调优。如杲内存增长缓慢,则可以设置一个稍大的值,大的阈值可以冇效降低CMS的触发频率,减少老年代冋收的次数可以较为明显地改善应用程序性能。反之,如果应用程序内存使用率增长很快,则应该降低这个阈值,以避免频繁触发老年代串行收集器。标记-清除算法将会造成大量内存碎片,离散的可用空间无法分配较大的对象。在这种情况下,即使堆内存仍然冇较大的剩余空间,也可能会被迫进行一次垃圾冋收,以换取一块可用的连续内存,这种现象对系统性能是相当不利的,为了解决这个问题,CMS收集器还提供了儿个用于内存压缩整理的算法。-XX:+UseCMSCompactAtFullCollection参数可以使CMS在垃圾收集完成后,进行一次内存碎片整理。内存碎片的整理并不是并发进行的。-XX:CMSFullGCsBeforcCompaction参数可以用于设定进行多少次CMS回收后,进行一次内存压缩。-XX:CMSInitiatingOccupancyFraction设置为100,同时设置-XX:+UseCMSCompactAtFulICollection和-XX:CMSFullGCsBeforeCompaction,Fl志输出如下:清单14.CMS垃圾回收器GC输出[GC[DefNew:825K->149K(4928K),0.0023384secs][Tenured:8704K->661K(10944K),0.0587725secs]9017K->661K(15872K),[Perm:374K->374K(12288K)],0.0612037secs][Times:user=0.01sys=0.02,rcal=0.06secs]Heap defnewgenerationtotal4992K,used179K[0x27010000,0x27570000,0x2c560000)edenspace4480K,4%used[0x27010000,0x2703cda&0x27470000)fromspace512K,0%used[0x27470000,0x27470000,0x274f0000)tospace512K,0%used[0x274f0000,0x274f0000,0x27570000)tenuredgenerationtotal10944K,used8853K[0x2c560000,0x2d010000,0x37010000)thespace10944K,80%used[0x2c560000,0x2ce057c&0x2ce05800,0x2d010000)compactingpermgentotal0x3b010000)thespace12288K,3%used0x37c10000)rospace10240K,51%used0x3bal0000)rwspace12288K,55%used0x3c610000)12288K,used[0x37010000,[0x3b010000,[0x3bal0000,374K[0x37010000,0x37cl0000,0x3706dbl0,0x3706dc00,0x3b543000,0x3b543000,0x3c0ae4f8,0x3c0ae600,•G1收集器(GarbageFirst)G1收集器的口标是作为一款服务器的垃圾收集器,因此,它在吞吐量和停顿控制上,预期要优于CMS收集器。与CMS收集器相比,G1收集器是基于标记-压缩算法的。因此,它不会产生空间碎片,也没冇必要在收集完成后,进行一次独占式的碎片整理工作。G1收集器还可以进行非常精确的停顿控制。它可以让开发人员指定当停顿时长为M时,垃圾冋收[I寸间不超过No使用参数-XX:+UnlockExperimentalVMOptions-XX:+UseGlGC来启用G1回收器,设置G1回收器的目标停顿时间:-XX:MaxGCPauscMills二20,-XX:GCPauscIntcrvalMills二200。收集器对系统性能的影响通过清单15所示代码运行1万次循环,每次分配512*100B空间,采用不同的垃圾回收器,输出程序运行所消耗的时间。清单15.性能测试代码importjava.util.HashMap;publicclassGCTimeTest{staticIlashMapmap二newIlashMapO;publicstaticvoidmain(String[]args){longbegintime二System・curTentTimeM订lis(); for(inti=0;i<10000;i++){if(map.size()*512/1024/1024〉二400){map.clear();//保护内存不溢出System,out.printin("cleanmap");}byte[]bl;for(intj=0;j<100;j++){bl=newbyte[512];map.put(System.nanoTime(),bl);//不断消耗内存}}longendtime=System.currentTimeMillis();System,out.printin(endtime-begintime);}}使用参数-Xmx512M-Xms512M-XX:+UseParNewGC运行代码,输出如下:cleanmap8565costtime二1655使用参数-Xmx512M-Xms512M-XX:+UseParallel01dGC-XX:ParallelGCThreads=8运行代码,输出如下:cleanmap8798costtime=1998使用参数-Xmx512M-Xms512M-XX:+UseSerialGC运行代码,输出如下:cleanmap8864costtime二1717使用参数~Xmx512M-Xms512M-XX:+UseConcMarkSweepGC运行代码,输岀如下:cleanmap8862costtime=1530上面例子的GC输出可以看出,采用不同的垃圾回收机制及设定不同的线程数,对于代码段的整体执行时间有较大的影响。需要读者有针对性地选用适合自己代码段的垃圾回收机制。 GC相关参数总结1.与串行冋收器和关的参数-XX:+UseSerialGC:在新生代和老年代使用串行回收器。-XX:+SuivivorRatio:设置eden区大小和survivor区大小的比例。-XX:+PrctcnurcSizcThrcshold:设置大对象直接进入老年代的阈值。当对象的大小超过这个值时,将直接在老年代分配。-XX:MaxTenuringThresho1d:设置对彖进入老年代的年龄的最大值。每一次MinorGC后,对象年龄就加1。任何大于这个年龄的对象,一定会进入老年代。2.与并行GC相关的参数-XX:+UscParNewGC:在新生代使用并行收集器。-XX:+UseParallel01dGC:老年代使用并行回收收集器。-XX:ParallelGCThreads:设置用于垃圾冋收的线程数。通常情况下可以和CPU数量相等。但在CPU数量比较多的情况下,设置相对较小的数值也是合理的。-XX:MaxGCPauseMills:设置最大垃圾收集停顿时间。它的值是一个大于0的整数。收集器在工作时,会调整Java堆大小或者其他一些参数,尽可能地把停顿时间控制在MaxGCPauseMills以内。-XX:GCTimeRatio:设置吞吐量大小,它的值是一个0-100之间的整数。假设GCTimeRatio的值为n,那么系统将花费不超过1/(1+n)的吋间用于垃圾收集。-XX:+UseAdaptiveSizePolicy:打开自适应GC策略。在这种模式下,新生代的大小,cdcn和survivor的比例、晋升老年代的对象年龄等参数会被口动调整,以达到在堆大小、吞吐量和停顿时间之间的平衡点。3.与CMS回收器相关的参数-XX:+UseConcMarkSweepGC:新生代使用并行收集器,老年代使用CMS+串行收集器。-XX:+ParallelCMSThreads:设定CMS的线程数量。-XX:+CMSlnitiatingOccupanc)?'Fraction:设置CMS收集器在老年代空间被使用多少后触发,默认为68%o-XX:+UseFullGCsBeforeCompaction:设定进行多少次CMS垃圾回收后,进行一次内存压缩。 -XX:+CMSClassUnloadingEnabled:允许对类元数据进行冋收。-XX:+CMSParal1eIRemarkEndab1e:启用并行重标记。-XX:CMSTnitatingPermOccupancyFraction:当永久区占用率达到这一百分比后,启动CMS回收(前提是-XX:+CMSC1assUn1oadingEnab1ed激活了)。-XX:UseCMSlnitatingOccupancyOnly:表示只在到达阈值的时候,才进彳亍CMS回收。-XX:+CMSIncrementalMode:使用增量模式,比较适合单CPU。1.与G1回收器相关的参数-XX:+UseGlGC:使用G1回收器。-XX:+UnlockExperimentalVMOptions:允许使用实验性参数。-XX:+MaxGCPauseM订Is:设置最大垃圾收集停顿时间。-XX:+GCPauseIntervalMi11s:设置停顿间隔时间。2.其他参数-XX:+DisableExplicitGC:禁用显示GC。纟士由】五士口卅!口通过木文的学习,读者可以掌握基木的JVM垃圾回收器设计原理及使用规范。基于笔者多年的工作经验,没冇哪一条优化是可以照本宣科的,它一定是基于您对JVM垃圾回收器工作原理及口身程序设计有一定了解前提卜,通过大量的实验来找岀最适合i己的优化方案。

当前文档最多预览五页,下载文档查看全文

此文档下载收益归作者所有

当前文档最多预览五页,下载文档查看全文
温馨提示:
1. 部分包含数学公式或PPT动画的文件,查看预览时可能会显示错乱或异常,文件下载后无此问题,请放心下载。
2. 本文档由用户上传,版权归属用户,天天文库负责整理代发布。如果您对本文档版权有争议请及时联系客服。
3. 下载前请仔细阅读文档内容,确认文档内容符合您的需求后进行下载,若出现内容与标题不符可向本站投诉处理。
4. 下载文档时可能由于网络波动等原因无法下载或下载错误,付费完成后未能成功下载的用户请联系客服处理。
关闭