JVM性能调优1:JVM性能调优理论及实践(收集整理) JVM性能调优1:JVM性能调优理论及实践(收集整理) JVM性能调优1:JVM性能调优理论及实践(收集整理)



JVM性能调优1:JVM性能调优理论及实践(收集整理)

本系列包括:


注:本文部分内容收集整理了网上的资料。

1.      内存结构


1.1.     分代结构图


注意:


JVM中,非堆内存,根据模式不同分为不同的几个部分。


-Server下:非堆包括:持久代和代码缓存(Code cache


-client下:非堆包括:持久代、代码缓存(Code cache)、Perm rwperm ro


2.      性能指标


对于垃圾收集的性能,主要有两个指标。


吞吐量throughput)指用来进行垃圾收集之外工作所用的时间占总时间的百分比,一般要通过长时间的观察和测量。吞吐量包括了分配内存所花费的时间在内(一般来说无需对分配进行调优)。


Throughput is the percentage of total time not spent in garbage collection,considered over long periods of time. Throughput includes time spent inallocation (but tuning for speed of allocation is generally not needed.) fromTuning GC with jdk 1.5


暂停Pause)指由于进行垃圾收集而导致应用无法响应的时间。


Pauses are the times when an application appears unresponsive becausegarbage collection is occurring.


Footprint is the working set of a process, measured in pages and cachelines. On systems with limited physical memory or many processes, footprint maydictate scalability.      Promptnessis the time between when an object becomes dead and when the memory becomesavailable, an important consideration for distributed systems, including remotemethod invocation (RMI). fromTuning GC with jdk 1.5


用户对于垃圾收集有不同的需求,例如,对于Web服务器应用来说,吞吐量是要着重考虑的,而暂停时间可能由于网络的反应时间而不那么明显;而对于一个交互式图形界面的应用来说,即使是短暂的暂停都会带来非常不好的用户体验。


In general, aparticular generation sizing chooses a trade-off between these considerations.For example, a very largeyoung generation may maximize throughput,but does so at the expense of footprint, promptness, and pause times.younggeneration pauses can be minimized by using a smallyoung generationat the expense of throughput. To a first approximation, the sizing of onegeneration does not affect the collection frequency and pause times for anothergeneration .fromTuning GC with jdk 1.5


通常来说,如何设置代的大小是在这些考虑因素之间作出的一个权衡。例如,将新生代设置得很大会得到很好的吞吐性能,但是会增加暂停时间;反之,较小的新生代设置会减小暂停时间,但是降低了吞吐量。


一个代的大小不应该影响在其他代上进行垃圾收集的频率和暂停时间。


3.      引用类型


对象引用类型分为强引用、软引用、弱引用和虚引用


3.1.     强引用


就是我们一般声明对象是时虚拟机生成的引用,强引用环境下,垃圾回收时需要严格判断当前对象是否被强引用,如果被强引用,则不会被垃圾回收。


3.2.     软引用


软引用一般被做为缓存来使用。与强引用的区别是,软引用在垃圾回收时,虚拟机会根据当前系统的剩余内存来决定是否对软引用进行回收。如果剩余内存比较紧张,则虚拟机会回收软引用所引用的空间;如果剩余内存相对富裕,则不会进行回收。换句话说,虚拟机在发生OutOfMemory时,肯定是没有软引用存在的。


3.3.     弱引用


弱引用与软引用类似,都是作为缓存来使用。但与软引用不同,弱引用在进行垃圾回收时,是一定会被回收掉的,因此其生命周期只存在于一个垃圾回收周期内。


3.4.     虚引用


 


4.      内存分配回收次序


新生代是类的诞生、成长、消亡的区域,一个类在这里产生,应用,最后被垃圾回收器收集,结束生命。


新生区又分为两部分:伊甸区(Eden space)和幸存者区(Survivorpace),所有的类都是在endennew出来的。幸存区有两个: 0区(Survivor 0 space)和1区(Survivor 1 space)。


Eden的空间用完时,程序又需要创建对象,JVM的垃圾回收器将对enden区进行垃圾回收,将enden区中的不再被其他对象所引用的对象进行销毁(次收集)。


然后将伊甸园中的剩余对象移动到幸存0区。若幸存0区也满了,再对该区进行垃圾回收(次收集),然后移动到1区。那如果1区也满了呢?(次收集)


再移动到养老区。


 



新生代采用复制算法,old区采用标记清除算法。


无论是复制算法还是标记清除算法,在垃圾收集期间都要暂停客户的应用程序(cms垃圾收集器除外,它在初始标记和重新标记时暂停,并发标记和并发清除时,客户线程不暂停)。


无论是复制算法还是标记清除算法,在最开始,倒要标记活动的对象,即对象的可达性分析,在这里,必须要求对象是一致的(即可达性分析期间,内存状态是冻结的);在cms收集器是,在初始标记(对root对象标记时,要求冻结)冻结,但此时会产生浮动垃圾,即在并发标记时,由分配了新的对象(因为没有冻结)。


综述,基本过程是,复制算法:标记(暂停)-->复制(暂停,因对象地址发生变化);标记清除整理算法:标记(暂停)-->清除整理(消除碎片,暂停,因对象地址发生变化)。


root对象标记很快,对内存扫描分析,可达性分析过程很慢。清除很慢(内存回收),内存整理、复制(慢)。


内存分配需要时间。


5.      对象标记算法(Object Marking Algorithms


5.1.     引用计数法(ReferenceCounting


堆中每一个对象都有一个引用计数。当新创建一个对象,或者有变量被赋值为这个对象的引用,则这个对象的引用计数加1;当一个对象的引用超过生存期或者被设置一个新的值时,这个对象的引用计数减1。当对象的引用计数变为0时,就可以被当作垃圾收集。


这种方法的好处是垃圾收集较快,适用于实时环境。缺点是这种方法无法监测出循环引用。例如对象A引用对象B,对象B也引用对象A,则这两个对象可能无法被垃圾收集器收集。因此这种方法是垃圾收集的早期策略,现在很少使用。


5.2.     根搜索算法(Garbage Collection Roots Tracing


5.2.1.    基本思想


这种方法把每个对象看作图中一个节点,对象之间的引用关系为图中各节点的邻接关系。垃圾收集器从一个或数个根结点遍历对象图,如果有些对象节点永远无法到达,则这个对象可以被当作垃圾回收。


容易发现,这种方法可以检测出循环引用,避免了引用计数法的缺点,较为常用。步骤如下:


  1. 选定一些对象,作为 GC Roots,组成基对象集;

  2. 由基对象集内的对象出发,搜索所有可达的对象;

  3. 其余的不可达的对象,就是可以被回收的对象。

    这里的可达不可达与图论中的定义一样,所有的对象被看做点,引用被看做有向连接,整个引用关系就是一个有向图。在引用计数法中提到的循环引用,其实就是有向图中有环的情况,即构成有向有环图。引用计数法不适用于有向有环图,而根搜索算法适用于所有有向图,包括有环的和无环的。


5.2.2.    GCRoots


如果你的逻辑思维够清晰,你会说一定与选取基对象集的方法有关。是的,没错。选取 GC Roots 组成基对象集,其实就是选取如下这些对象:


《深入理解 Java 虚拟机:JVM高级特性与最佳实践》一书中提到的 GC Roots 为:


1.     方法区(Method Area,即 Non-Heap)中的类的 static 成员引用的对象,和 final成员引用的对象;


2.     Java方法栈(Java Method Stack)的局部变量表(Local Variable Table)中引用的对象;


3.     原生方法栈(Native Method Stack)中 JNI中引用的对象。


但显然不够全面,[参考2]中提到的要更全面:(March 6th,2012 update


1.     由系统类加载器加载的类相应的对象:这些类永远不会被卸载,且这些类创建的对象都是 static 的。注意用户使用的类加载器加载的类创建的对象,不属于 GC Roots,除非是 java.lang.Class 的相应实例有可能会称为其他类的 GC Roots


2.     正在运行的线程。


3.     Java方法栈(Java Method Stack)的局部变量表(Local Variable Table)中引用的对象。


4.     原生方法栈(Native Method Stack)的局部变量表(Local Variable Table)中引用的对象。


5.     JNI中引用的对象。


6.     同步监控器使用的对象。


7.      JVM  GC控制的对象:这些对象是用于 JVM内部的,是实现相关的。一般情况下,可能包括系统类加载器(注意与“1”不一样,“1”中是 objects created by the classes loaded bysystem class loaders,这里是 theobjects, corresponding instances of system class loaders)、JVM内部的一些重要的异常类的对象、异常句柄的预分配对象和在类加载过程中自定义的类加载器。不幸的是,JVM并不提供这些对象的任何额外的详细信息。因此这些实现相关的内容,需要依靠分析来判定。


所以这个算法实施起来有两部分,第一部分就是到 JVM 的几个内存区域中找对象,第二部分就是运用图论算法


 


 


 


 


6.      垃圾回收算法


6.1.     标记-清除(Mark-Sweep



此算法执行分两阶段。第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除。此算法需要暂停整个应用,同时,会产生内存碎片。


6.2.     标记-整理(Mark-Compact


此算法结合了标记-清除复制两个算法的优点。也是分两阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,把清除未标记对象并且把存活对象压缩到堆的其中一块,按顺序排放。此算法避免了标记-清除的碎片问题,同时也避免了复制算法的空间问题。


6.3.     复制(Copying


此算法把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中。次算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理,不会出现碎片问题。当然,此算法的缺点也是很明显的,就是需要两倍内存空间


6.4.     增量收集算法


增量收集器把堆栈分为多个域,每次仅从一个域收集垃圾。这会造成较小的应用程序中断。


6.5.     分代收集算法


这种收集器把堆栈分为两个或多个域,用以存放不同寿命的对象。虚拟机生成的新对象一般放在其中的某个域中。过一段时间,继续存在的对象将获得使用期并转入更长寿命的域中。分代收集器对不同的域使用不同的算法以优化性能。这样可以减少复制对象的时间。


6.6.     并发收集算法


并发收集器与应用程序同时运行。这些收集器在某点上(比如压缩时)一般都不得不停止其他操作以完成特定的任务,但是因为其他应用程序可进行其他的后台操作,所以中断其他处理的实际时间大大降低。


6.7.     并行收集器


并行收集器使用某种传统的算法并使用多线程并行的执行它们的工作。在多CPU机器上使用多线程技术可以显著的提高java应用程序的可扩展性。


6.8.     自适应收集器


根据程序运行状况以及堆的使用状况,自动选一种合适的垃圾回收算法。这样可以不局限与一种垃圾回收算法。


6.9.     火车增量算法


垃圾收集算法一个很大的缺点就是难以控制垃圾回收所占用的CPU时间,以及何时需要进行垃圾回收。火车算法是分代收集器所用的算法,目的是在成熟对象空间中提供限定时间的渐进收集。目前应用于SUN公司的Hotspot虚拟机上。


在火车算法中,内存被分为块,多个块组成一个集合。为了形象化,一节车厢代表一个块,一列火车代表一个集合,见图一


图一


注意每个车厢大小相等,但每个火车包含的车厢数不一定相等。垃圾收集以车厢为单位,收集顺序依次为1.11.21.31.42.12.22.33.13.23.3。这个顺序也是块被创建的先后顺序。


垃圾收集器先从块1.1开始扫描直到1.4,如果火车1四个块中的所有对象没有被火车2和火车3的对象引用,而只有火车1内部的对象相互引用,则整个火车1都是垃圾,可以被回收。


如图二,车厢1.1中有对象A和对象B1.3中有对象C1.4中有对象D,车厢2.2中有对象E,车厢3.3中有对象F。在火车1中,对象C引用对象A,对象B引用对象D,可见,火车2和火车3没有引用火车1的对象,则整个火车1都是垃圾。


图二


如果火车1中有对象被其它火车引用,见图三,扫描车厢1.1时发现对象A被火车2中的E引用,则将对象A从车厢1.1转移到车厢2.2,然后扫描A引用的对象D,把D也转移到车厢2.2,然后扫描D,看D是否引用其它对象,如果引用了其它对象则也要转移,依次类推。扫描完火车1的所有对象后,剩下的没有转移的对象都是垃圾,可以把整个火车1都作为垃圾回收。注意如果在转移时,如果车厢2.2空间满了,则要在火车2末尾开辟新的车厢2.4,将新转移的对象都放到2.4,即火车的尾部)


图三


补充说明:垃圾回收器一次只扫描一个车厢。图三中的对象BC并不是立即被回收,而是先会被转移到火车1的尾部车厢。即扫描完1.1后,B被转移到火车1尾部,扫描完1.3后,C被转移到车尾。等垃圾收集器扫描到火车1尾部时,如果仍然没有外部对象引用它们,则BC会被收集。


火车算法最大的好处是它可以保证大的循环结构可以被完全收集,因为成为垃圾的循环结构中的对象,无论多大,都会被移入同一列火车,最终一起被收集。还有一个好处是这种算法在大多数情况下可以保证一次垃圾收集所耗时间在一定限度之内,因为一次垃圾回收只收集一个车厢,而车厢的大小是有限度的。


 


 


7.      触发垃圾收集的条件


由于对象进行了分代处理,因此垃圾回收区域、时间也不一样。GC有两种类型:Scavenge GCFull GC


7.1.     次收集ScavengeGC


一般情况下,当Enden区对象已满,或当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区进行,不会影响到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden去能尽快空闲出来。


Survivor区,当达到设置的预定值(默认50%),则进行一次垃圾次收集。


7.2.     全收集FullGC


对整个堆进行整理,包括YoungTenuredPermFull GC因为需要对整个对进行回收,所以比Scavenge GC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分工作就是对于FullGC的调节。有如下原因可能导致Full GC


·年老代(Tenured)被写满


·持久代(Perm)被写满


· System.gc()被显示调用


·上一次GC之后Heap的各域分配策略动态变化


8.      垃圾收集器类型


8.1.     串行收集器


用单线程处理所有垃圾回收工作,因为无需多线程交互,所以效率比较高。但是,也无法使用多处理器的优势,所以此收集器适合单处理器机器。当然,此收集器也可以用在小数据量(100M左右)情况下的多处理器机器上。可以使用-XX:+UseSerialGC打开。


8.2.     并行收集器


注:图有问题,是并发垃圾收集器的图。


并行垃圾回收,因此可以减少垃圾回收时间。一般在多线程多处理器机器上使用。


8.3.     并发垃圾收集


并发收集器主要减少年老代的暂停时间,他在应用不停止的情况下使用独立的垃圾回收线程,跟踪可达对象。在每个年老代垃圾回收周期中,在收集初期并发收集器会对整个应用进行简短的暂停,在收集中还会再暂停一次。第二次暂停会比第一次稍长,在此过程中多个线程同时进行垃圾回收工作。


浮动垃圾:由于在应用运行的同时进行垃圾回收,所以有些垃圾可能在垃圾回收进行完成时产生,这样就造成了“Floating Garbage”,这些垃圾需要在下次垃圾回收周期时才能回收掉。所以,并发收集器一般需要20%的预留空间用于这些浮动垃圾。


Concurrent Mode Failure:并发收集器在应用运行时进行收集,所以需要保证堆在垃圾回收的这段时间有足够的空间供程序使用,否则,垃圾回收还未完成,堆空间先满了。这种情况下将会发生并发模式失败,此时整个应用将会暂停,进行垃圾回收。


启动并发收集器:因为并发收集在应用运行时进行收集,所以必须保证收集完成之前有足够的内存空间供程序使用,否则会出现“Concurrent Mode Failure”。通过设置-XX:CMSInitiatingOccupancyFraction=<N>指定old区达到百分之多少时开始执行并发收集。


9.      垃圾收集器介绍


9.1.     串行垃圾收集


Serial Collector是指任何时刻都只有一个线程进行垃圾收集,这种策略有一个名字“stop the whole world",它需要停止整个应用的执行。这种类型的收集器适合于单器CPU的机器。


Serial Copying Collector


此种GC-XX:UseSerialGC选项配置,它只用于新生代对象的收集。1.5.0以后.


-XX:MaxTenuringThreshold来设置对象复制的次数。当eden空间不够的时候,GC会将eden的活跃对象和一个名叫From survivor空间中尚不够资格放入Old代的对象复制到另外一个名字叫To Survivor的空间。而此参数就是用来说明到底From survivor中的哪些对象不够资格,假如这个参数设置为31,那么也就是说只有对象复制31次以后才算是有资格的对象。


这里需要注意几个个问题:


  • From SurvivorTo survivor的角色是不断的变化的,同一时间只有一块空间处于使用状态,这个空间就叫做From Survivor区,当复制一次后角色就发生了变化。

  • 如果复制的过程中发现To survivor空间已经满了,那么就直接复制到old generation.

  • 比较大的对象也会直接复制到Old generation,在开发中,我们应该尽量避免这种情况的发生。

     

     

    并行垃圾收集(Parallel Collector

    Parallel Copying Collector

    -XX:+UseParNewGC用来设置年轻代为并发收集【多CPU】,如果你的服务器有多个CPU,你可以开启此参数;开启此参数,多个CPU可并发进行垃圾回收,可提高垃圾回收的速度。此参数和-XX:ParallelGCThreads搭配使用。适用于1.4以后版本。


Parallel Mark-Compact Collector


此种GC-XX:+UseParallelOldGC参数配置,此GC主要用于老生代对象的收集。适用于1.6.0以后版本。


Parallel scavenging Collector


-XX:+UseParallelGC它是对新生代对象的垃圾收集器。年轻代使用并行收集,而年老代仍旧使用串行收集。可提高系统的吞吐量。此参数和-XX:ParallelGCThreads搭配使用。适用于1.4以后版本。它比较适合于对吞吐量高于暂停时间的场合。


 


采用多线程并行(Parallel)进行垃圾收集。


9.3.     并发垃圾收集器(Concurrent Collector)


此种GC可以用参数-XX:+UseConcMarkSweepGC配置,此GC主要用于老生代和Perm代的收集。


Concurrent Collector通过并发的方式进行垃圾收集,这样就减少了垃圾收集器收集一次的时间,这种GC在实时性要求高于吞吐量的时候比较有用。


在应用运行的同时,并发(Concurrent)进行垃圾收集。


  1. 配置垃圾回收线程数量

             -XX:ParallelGCThreads年轻代并行垃圾收集的前提下(对并发也有效果)的线程数,增加并行度,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。
    例如:-XX:ParallelGCThreads=2

  2. 配置垃圾收集器需要注意的地方

    垃圾收集器不可重复配置,例如已经配置了新生代并行收集(-XX:+UseParNewGC),此时不能配置任何对新生代的垃圾收集。可以配置对旧生代和持久对象的垃圾收集。

    例如:

    -XX:+UseParNewGC-XX:+UseConcMarkSweepGC -XX:ParallelGCThreads=2

    可组合适用。

    增量

    配合并发垃圾收集,适用于单cpu的架构,sun不建议使用。

     

    自适应

    自适应垃圾收集,运行在并行垃圾收集上。在并行收集时对于收集时间、分配比例、收集之堆的空闲空间等数据进行统计分析,然后以此为依据调整新生代和旧生代的大小以达到最佳效果。

     

     

    小结

             J2SE平台1.4.2版本开始,加入了另外3种垃圾收集器,都是着重提高吞吐能力,降低垃圾收集时的暂停时间。

             1. 吞吐收集器(throughput collector):命令行参数:-XX:+UseParallelGC。在新生代使用并行收集策略,在旧生代和默认收集器相同。

             吞吐收集器和默认的收集器类似,都是分代收集器。不同之处就在于吞吐收集器用多线程进行次要收集,

            J2SE平台1.4.1版本开始,吞吐收集器就具有了一个特征,就是大小自适应(参数-XX:

    +UseAdaptiveSizePolicy),这个选项默认是打开的。该特征对于收集时间、分配比例、收集之   堆的空闲空间等数据进行统计分析,然后以此为依据调整新生代和旧生代的大小以达到最佳效果。可以使用-verbose:gc来查看堆的大小。

             -XX:+AggressiveHeap选项会检测主机的资源(内存大小、处理器数量),然后调整相关的参数,使得长时间运行的、内存申请密集的任务能够以最佳状态运行。该选项最初是为拥有大量内存和很多处理器的主机而设计的,但是从J2SE1.4.1以及其后继版本来看,即使是对于那些只有4CPU       主机,该选项都是很有帮助的。因此,吞吐收集器(-XX:+UseParallelGC)、大小自适应 -XX:+UseAdaptiveSizePolicy)以及本选项(-XX:+AggressiveHeap)经常结合在一起使用。要使    用本选项,主机上至少要有256M的物理内存,堆内存的最初大小是基于物理内存计算出来的,然后 会根据需要尽可能的利用物理内存。

           2. 并发收集器(concurrent low pause collector):命令行参数:-XX:+UseConcMarkSweepGC。在旧生代使用并发收集策略,大部分收集工作都是和应用并发进行的,在进行收集的时候,应用的暂停时间很短。如果综合使用-XX:+UseParNewGC-XX:+UseConcMarkSweepGC,那么在新生代上使用并行的收集策略。

             3. 增量收集器(incremental low pause collector):命令行参数:-Xincgc。使用增量收集器要谨慎,他只是在每次进行次要收集的时候对旧生代进行一部分的收集,这样就把主要收集所带来的较长时间的停顿分散到多次的次要收集。但是,考虑到总共的吞吐,可能比旧生代上默认的收集还要慢。

    注意,-XX:+UseParallelGCXX:+UseConcMarkSweepGC不能同时使用。对于J2SE1.4.2版本会检查垃圾收集相关参数组合的合法性,但是对于之前的版本没有这个检查,可能会导致不可预知的错误。

    垃圾回收器相关参数

    串行垃圾收集器参数

     


参数

说明

-XX:+UseSerialGC

设置串行收集器

 

 


 


10.2.  并行垃圾收集器参数


参数

说明

-XX:+UseParallelGC

选择垃圾收集器为并行收集器,此配置仅对年轻代有效,即上述配置下,年轻代使用并行收集,而老年代仍旧使用串行收集。采用了多线程并行管理和回收垃圾对象,提高了回收效率,提高了服务器的吞吐量,适合于多处理器的服务器。

-XX:ParallelGCThreads

配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。

-XX:+UseParallelOldGC

采用对于老年代并发收集的策略,可以提高收集效率。JDK6.0支持对老年代并行收集。

-XX:MaxGCPauseMillis

设置每次年轻代并行收集最大暂停时间,如果无法满足此时间,JVM会自动调整年轻代大小以满足此值。

-XX:+UseAdaptiveSizePolicy

设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低响应时间或者收集频率等,此值建议使用并行收集器时,一直打开。

-XX:+UseParNewGC

指定在 New Generation使用 parallel collector, UseParallelGC gc的升级版本 ,有更好的性能或者优点 ,可以和 CMS gc一起使用


 


 


10.3.  并发垃圾收集器参数


参数

说明

-XX:+UseConcMarkSweepGC

指定在老年代使用 concurrent cmark sweep gcgc thread app thread并行 ( init-mark remark pause app thread)app pause时间较短 ,适合交互性强的系统 , web server。它可以并发执行收集操作,降低应用停止时间,同时它也是并行处理模式,可以有效地利用多处理器的系统的多进程处理。

-XX:+UseCMSCompactAtFullCollection

打开对老年代的压缩。可能会影响性能,但是可以消除碎片,FULL GC的时候,压缩内存, CMS是不会移动内存的,因此,这个非常容易产生碎片,导致内存不够用,因此,内存的压缩这个时候就会被启用。增加这个参数是个好习惯。

-XX:+CMSIncrementalMode

设置为增量模式。适用于单CPU情况

-XX:CMSFullGCsBeforeCompaction

由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生碎片,使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。

-XX:+CMSClassUnloadingEnabled

使CMS收集持久代的类,而不是fullgc

-XX:+CMSPermGenSweepingEnabled

使CMS收集持久代的类,而不是fullgc

-XX:-CMSParallelRemarkEnabled

在使用 UseParNewGC的情况下 ,尽量减少 mark的时间。

-XX:CMSInitiatingOccupancyFraction

说明老年代到百分之多少满的时候开始执行对老年代的并发垃圾回收(CMS),这个参数设置有很大技巧,基本上满足公式:

(Xmx-Xmn)*(100-CMSInitiatingOccupancyFraction)/100>=Xmn

时就不会出现promotion failed。在我的应用中Xmx6000Xmn500,那么Xmx-Xmn5500兆,也就是老年代有5500兆,CMSInitiatingOccupancyFraction=90说明老年代到90%满的时候开始执行对老年代的并发垃圾回收(CMS),这时还剩10%的空间是5500*10%=550兆,所以即使Xmn(也就是年轻代共500兆)里所有对象都搬到老年代里,550兆的空间也足够了,所以只要满足上面的公式,就不会出现垃圾回收时的promotion failed

如果按照Xmx=2048,Xmn=768的比例计算,则CMSInitiatingOccupancyFraction的值不能超过40,否则就容易出现垃圾回收时的promotion failed

-XX:+UseCMSInitiatingOccupancyOnly

指示只有在老年代在使用了初始化的比例后 concurrent collector 启动收集


 


11.   JVM内存溢出类型


对于JVM的内存写过的文章已经有点多了,而且有点烂了,不过说那么多大多数在解决OOM的情况,于此,本文就只阐述这个内容,携带一些分析和理解和部分扩展内容,也就是JVM宕机中的一些问题,OK,下面说下OOM的常见情况:


第一类内存溢出,就是堆栈溢出:关键字:java.lang.OutOfMemoryError: ......javaheap space.....


也就是当你看到heap相关的时候就肯定是堆栈溢出了,此时如果代码没有问题的情况下,适当调整-Xmx-Xms是可以避免的,不过一定是代码没有问题的前提,为什么会溢出呢,要么代码有问题,要么访问量太多并且每个访问的时间太长或者数据太多,导致数据释放不掉,因为垃圾回收器是要找到那些是垃圾才能回收,这里它不会认为这些东西是垃圾,自然不会去回收了;主意这个溢出之前,可能系统会提前先报错关键字为:


java.lang.OutOfMemoryError:GC over headlimit exceeded


这种情况是当系统处于高频的GC状态,而且回收的效果依然不佳的情况,就会开始报这个错误,这种情况一般是产生了很多不可以被释放的对象,有可能是引用使用不当导致,或申请大对象导致,但是java heap space的内存溢出有可能提前不会报这个错误,也就是可能内存就直接不够导致,而不是高频GC.


第二类内存溢出,PermGen的溢出,或者PermGen满了的提示,你会看到这样的关键字:关键信息为:java.lang.OutOfMemoryError: PermGenspace


原因:系统的代码非常多或引用的第三方包非常多、或代码中使用了大量的常量、或通过intern注入常量、或者通过动态代码加载等方法,导致常量池的膨胀,虽然JDK 1.5以后可以通过设置对永久带进行回收,但是我们希望的是这个地方是不做GC的,它够用就行,所以一般情况下今年少做类似的操作,所以在面对这种情况常用的手段是:增加-XX:PermSize-XX:MaxPermSize的大小。


第三类内存溢出:在使用ByteBuffer中的allocateDirect()的时候会用到,很多javaNIO的框架中被封装为其他的方法溢出关键字:java.lang.OutOfMemoryError: Directbuffer memory


如果你在直接或间接使用了ByteBuffer中的allocateDirect方法的时候,而不做clear的时候就会出现类似的问题,常规的引用程序IO输出存在一个内核态与用户态的转换过程,也就是对应直接内存与非直接内存,如果常规的应用程序你要将一个文件的内容输出到客户端需要通过OS的直接内存转换拷贝到程序的非直接内存(也就是heap中),然后再输出到直接内存由操作系统发送出去,而直接内存就是由OS和应用程序共同管理的,而非直接内存可以直接由应用程序自己控制的内存,jvm垃圾回收不会回收掉直接内存这部分的内存,所以要注意了哦。


如果经常有类似的操作,可以考虑设置参数:-XX:MaxDirectMemorySize


第四类内存溢出错误:溢出关键字:java.lang.StackOverflowError


这个参数直接说明一个内容,就是-Xss太小了,我们申请很多局部调用的栈针等内容是存放在用户当前所持有的线程中的,线程在jdk 1.4以前默认是256K1.5以后是1M,如果报这个错,只能说明-Xss设置得太小,当然有些厂商的JVM不是这个参数,本文仅仅针对Hotspot VM而已;不过在有必要的情况下可以对系统做一些优化,使得-Xss的值是可用的。


第五类内存溢出错误:溢出关键字:java.lang.OutOfMemoryError: unable tocreate new native thread


上面第四种溢出错误,已经说明了线程的内存空间,其实线程基本只占用heap以外的内存区域,也就是这个错误说明除了heap以外的区域,无法为线程分配一块内存区域了,这个要么是内存本身就不够,要么heap的空间设置得太大了,导致了剩余的内存已经不多了,而由于线程本身要占用内存,所以就不够用了,说明了原因,如何去修改,不用我多说,你懂的。


第六类内存溢出:溢出关键字java.lang.OutOfMemoryError: request {}byte for {}out of swap


这类错误一般是由于地址空间不够而导致。


六大类常见溢出已经说明JVM99%的溢出情况,要逃出这些溢出情况非常困难,除非一些很怪异的故障问题会发生,比如由于物理内存的硬件问题,导致了code cache的错误(在由byte code转换为native code的过程中出现,但是概率极低),这种情况内存会被直接crash掉,类似还有swap的频繁交互在部分系统中会导致系统直接被crash掉,OS地址空间不够的话,系统根本无法启动,呵呵;JNI的滥用也会导致一些本地内存无法释放的问题,所以尽量避开JNIsocket连接数据打开过多的socket也会报类似:IOException: Too many open files等错误信息。


JNI就不用多说了,尽量少用,除非你的代码太牛B了,我无话可说,呵呵,这种内存如果没有在被调用的语言内部将内存释放掉(如C语言),那么在进程结束前这些内存永远释放不掉,解决办法只有一个就是将进程kill掉。


另外GC本身是需要内存空间的,因为在运算和中间数据转换过程中都需要有内存,所以你要保证GC的时候有足够的内存哦,如果没有的话GC的过程将会非常的缓慢。


 


12.   JAVA虚拟机参数分类说明


12.1.  标准参数-


所谓的Java虚拟机标准参数指的就是所有的虚拟机实现都应该支持的参数,这部分参数基本上都是对虚拟机基本能力的调整,包括对运行模式、垃圾收集信息显示、显示版本信息、断言开关等,下面是以Solaris1.4.2版本为例:


参数

使用说明

备注

-d32

-d64

指明该Java VM是运行与32位环境还是64位环境,默认是运行在32位环境下的,如果是配置了64位模式则需要操作系统也必须是64位的,当然CPU更需要是64位的。另外如果我们选择了-server参数,则就暗含了64位模式。

因为64CPU兼容32位操作系统,而64位操作系统又是兼容32位执行程序

-client

-server

设置该JVM运行与Client或者Server Hotspot模式,这两种模式从本质上来说是在JVM中运行不同的JIT(运行时编译模块)代码,并且两者在JVM内部的接口是一致的。客户端模式优化的是系统启动时间更快,而服务端模式的优化则更关注与系统的整体性能。一般来说Client选项用于GUI的应用,Server选项多用于后台服务器应用。

另外两者在编译策略、垃圾收集策略、堆使用上也有所不同

-hotspot

Hotspot类型的JVM中缺省使用,缺省为Client Hotspot模式。

 

-cp

-classpath

指明JVM启动时要加载的类文件路径,Java虚拟机进程在启动时就会按照该参数后面指明的路径查找*.zip*.jar*.class文件,然后将这些包中的类文件加载到内存中。

JVM加载类文件的顺序是

-D<name>=<value>

设置系统属性的值,该参数是的设计是为了满足Java应用程序员与JVM进行参数传递的手段之一,另一种是通过应用级参数(argument)来实现。

Java程序员可以在程序内调用system.getProperty来获取用户通过-D参数传进来的系统属性信息。而命令行参数就是是JVM传递给main函数的调用参数

 

-verbose:class

 

-verbose:gc

 

-verbose:jni

打印详细信息,目前支持打印类加载信息:class、垃圾收集信息:gc、以及本地方法调用信息:jni,如果选择了此选项,则JVM会在命令行打印出上述信息;

对于测试中的系统可以通过打开:gc开关,查看JVM每次垃圾收集的详细信息来判断系统内存消耗情况,如果系统垃圾收集的很频繁,而且每次都回收了大量的内存,则说明系统内存消耗很大,对象的创建和湮灭很频繁,而如果堆内存一直保持着增长的话,说明可能存在内存“泄漏”。

-version

-showversion

-version选项是显示版本信息后JVM退出

-showversion选项是显示版本信息后JVM继续运行

 

-esa

-enableassertions

打开系统中每个类的断言开关

该选项用于程序开发、调试过程

-da

-disableassertions

关闭系统中每个类的断言开关

该选项用于程序开发、调试过程

 

 

 


        1. JVM标准参数集


12.2.  扩展参数-X


所谓的Java虚拟机非标准参数指的就是一些特有的虚拟机实现所支持,下面以Solaris1.4.2版本为例介绍一些扩展的虚拟机运行参数,其中对Hotspot VM相关的参数是我们进行性能调整的重点。


参数

使用说明

备注

-Xmixed

JVM执行模式的设置参数,混合模式即支持Hotspot即时编译的运行模式

支持HotspotJVM缺省都是运行于混合模式的。

-Xint

设置JVM的执行模式为解释执行模式,纯解释执行的JVM对多数应用来说基本上时没有意义的,仅仅可能会在一些嵌入式系统中应用

 

-Xbootclasspath

设置初始类装载器的装载路径

 

-Xnoclassgc

设置不执行类垃圾收集

 

-Xincgc

设置是否启动火车垃圾收集算法

 

-Xloggc:<file>

设置是否将GC信息写入日志文件

 

-Xbatch

设置不执行后台编译

 

-Xms<size>

设置JVM启动时初始内存堆的大小

 

-Xmx<size>

设置JVM启动后动态申请堆内存的最大堆空间

 

-Xss<size>

设置JVM最大线程栈的空间大小

 

-Xprof

是否打印输出性能统计数据

 

-Xrunhprof

设置是否启动heapcpu等性能统计监控功能(详细见下表)

 

-Xdebug

设置是否启动远程调试功能

 

-Xfuture

 

 

-Xrs

设置是否屏蔽操作系统信号

 

-Xcheck:jni

设置对于本地调用是否执行额外检查

 


        1. JVM扩展参数集


Java HotspotGC相关参数介绍,下面以Solaris1.4.2版本为例,对于以–X打头的非标准参数,是不能保证在每个JVM的实现中都支持的,而且关于这些参数行为的改变都不会得到通知。


12.3.  非稳定(Stable)参数(-XX


而对于以–XX打头的非标准参数来说,它们中大多数都是和具体的操作系统支持有关的,而且有些甚至需要特殊的系统访问权限,而且这些参数也是遵循上述的改变不通知原则的。在使用中需要特别注意。


Sun的文档中,又分为三类:


行为参数Behavioral Options):用于改变jvm的一些基础行为;
性能调优Performance Tuning):用于jvm的性能调优;
调试参数Debugging Options):一般用于打开跟踪、打印、输出等jvm参数,用于显示jvm更加详细的信息;


12.3.1. 行为参数


 


参数及其默认值

描述

-XX:-DisableExplicitGC

禁止调用System.gc();但jvmgc仍然有效

-XX:+MaxFDLimit

最大化文件描述符的数量限制

-XX:+ScavengeBeforeFullGC

新生代GC优先于Full GC执行

-XX:+UseGCOverheadLimit

在抛出OOM之前限制jvm耗费在GC上的时间比例

-XX:-UseConcMarkSweepGC

对老生代采用并发标记交换算法进行GC

-XX:-UseParallelGC

启用并行GC

-XX:-UseParallelOldGC

Full GC启用并行,当-XX:-UseParallelGC启用时该项自动启用

-XX:-UseSerialGC

启用串行GC

-XX:+UseThreadPriorities

启用本地线程优先级


 


上面表格中黑体的三个参数代表着jvmGC执行的三种方式,即串行、并行、并发;


串行(SerialGCjvm的默认GC方式,一般适用于小型应用和单处理器,算法比较简单,GC效率也较高,但可能会给应用带来停顿;


并行(ParallelGC是指GC运行时,对应用程序运行没有影响,GCapp两者的线程在并行执行,这样可以最大限度不影响app的运行;


并发(ConcMarkSweepGC是指多个线程并发执行GC,一般适用于多处理器系统中,可以提高GC的效率,但算法复杂,系统消耗较大;


12.3.2. 性能调优


 


参数及其默认值

描述

-XX:LargePageSizeInBytes=4m

设置用于Java堆的大页面尺寸

-XX:MaxHeapFreeRatio=70

GCjava堆中空闲量占的最大比例

-XX:MaxNewSize=size

新生成对象能占用内存的最大值

-XX:MaxPermSize=64m

老生代对象能占用内存的最大值

-XX:MinHeapFreeRatio=40

GCjava堆中空闲量占的最小比例

-XX:NewRatio=2

新生代内存容量与老生代内存容量的比例

-XX:NewSize=2.125m

新生代对象生成时占用内存的默认值

-XX:ReservedCodeCacheSize=32m

保留代码占用的内存容量

-XX:ThreadStackSize=512

设置线程栈大小,若为0则使用系统默认值

-XX:+UseLargePages

使用大页面内存


 


12.3.3. 调试/打印信息参数


参数及其默认值

描述

-XX:-CITime

打印消耗在JIT编译的时间

-XX:ErrorFile=./hs_err_pid<pid>.log

保存错误日志或者数据到文件中

-XX:-ExtendedDTraceProbes

开启solaris特有的dtrace探针

-XX:HeapDumpPath=./java_pid<pid>.hprof

指定导出堆信息时的路径或文件名

-XX:HeapDumpOnOutOfMemoryError

当首次遭遇OOM时导出此时堆中相关信息

-XX:OnError="<cmd args>;<cmd args>"

出现致命ERROR之后运行自定义命令

-XX:OnOutOfMemoryError="<cmd args>;<cmd args>"

当首次遭遇OOM时执行自定义命令

-XX:-PrintClassHistogram

遇到Ctrl-Break后打印类实例的柱状信息,与jmap -histo功能相同

-XX:-PrintConcurrentLocks

遇到Ctrl-Break后打印并发锁的相关信息,与jstack -l功能相同

-XX:-PrintCommandLineFlags

打印在命令行中出现过的标记

-XX:-PrintCompilation

当一个方法被编译时打印相关信息

-XX:-PrintGC

每次GC时打印相关信息

-XX:-PrintGC Details

每次GC时打印详细信息

-XX:-PrintGCTimeStamps

打印每次GC的时间戳

-XX:-TraceClassLoading

跟踪类的加载信息

-XX:-TraceClassLoadingPreorder

跟踪被引用到的所有类的加载信息

-XX:-TraceClassResolution

跟踪常量池

-XX:-TraceClassUnloading

跟踪类的卸载信息

-XX:-TraceLoaderConstraints

跟踪类加载器约束的相关信息


 


 


12.3.4. 参数汇总(不含调试和垃圾收集参数)


 


参数

使用说明

备注

-XX:+AggressiveHeap

长时间大内存使用的优化,能检查计算资源(内存,处理器数量),至少需要256MB内存大量的CPU/内存,(在1.4.14CPU的机器上已经显示有提升)

 

-XX:+AggressiveOpts

加快编译

 

-XX:-AllowUserSignal

Handlers

允许用户在应用层设置信号处理回调函数

 

-XX:AltStackSize=16384

预备信号栈的大小

 

-XX:-CITime

设置Hotspot的一次即时编译所需要的最大时间

 

-XX:CompileThreshold

=10000

设置方法是否进行即时编译的调用次数的下限值,-server选项的缺省值为10000-client选项的缺省值为1500

即:当该方法的被调用测试多于该值时,则该方法就会被JIT即时编译器编译成机器代码在内存中执行

-XX:+DisableExplicitGC

屏蔽程序主动垃圾收集的函数system.gc()

 

-XX:FreqInlineSize=size

限制经常使用的动态编译的函数的虚拟机指令的最大数量,

 

-Xincgc

在垃圾收集中使用火车算法

 

-Xint

不启用即时编译(JIT)功能,仅仅解释执行

缺省为不选的

-XX:LargePageSizeInBytes

内存页的大小,不可设置过大,会影响Perm的大小。

 

 

 

 

-XX:MaxHeapFreeRatio

=<Maximum>

JVM中堆空间的最大空闲百分比,缺省为70%GC停止回收空间的上限值

即:一旦当前堆内存空闲空间百分比超过总空间70%时,GC暂停垃圾收集

-XX:MinHeapFreeRatio

=<Minimum>

JVM中堆空间的最小空闲百分比,缺省为40%GC开始回收空间的下限值

即:一旦当前内存堆中内存空闲小于40%时,GC则恢复垃圾收集

-XX:MaxInlineSize=size

限制动态编译的内联函数的虚拟机指令的最大数量

 

-XX:+MaxFDLimit

设置JVM进程打开最大文件句柄数(Solaris only

 

-XX:MaxNewSize=32m

在为新生代对象分配内存值,每块内存的最大值。

按代垃圾收集中使用

-XX:MaxTenuringThreshold=30

在存活区之间Copy的次数,超过该次数则移至Old区。

 

-Xmn

为新生代分配的内存大小。

 

-XX:NewRatio=2

新生代与老一代空间的比率,-XX:NewRatio = 2表示 Eden:old = 1:2SUN Parc –server中是21Intel中是121

 

-XX:NewSize=2228224

新一代的缺省申请空间的值

对于大型应用服务器系统这个值2K一般情况下需要调整大一些

-Xnoincgc

在垃圾收集中不使用火车算法

 

-XX:PreBlockSpin=10

 

 

-XX:-PrintTenuring

Distribution

打印使用年限

 

-XX:ReservedCodeCache

Size=32m

设置内存中保留代码缓冲区的大小

 

-XX:SoftRefLRUPolicyMSPerMB

相对于客户端模式的虚拟机(-client选项),当使用服务器模式的虚拟机时(-server选项),对于软引用(soft reference)的清理力度要稍微差一些。可以通过增大-XX:SoftRefLRUPolicyMSPerMB来降低收集频率。默认值是 1000,也就是说每秒一兆字节。Soft reference在虚拟机中比在客户集中存活的更长一些。其清除频率可以用命令行参数-XX:SoftRefLRUPolicyMSPerMB=<N>来控制,这可以指定每兆堆空闲空间的 soft reference保持存活(一旦它不强可达了)的毫秒数,这意味着每兆堆中的空闲空间中的 soft reference会(在最后一个强引用被回收之后)存活1秒钟。注意,这是一个近似的值,因为 soft reference只会在垃圾回收时才会被清除,而垃圾回收并不总在发生。

 

-XX:SurvivorRatio=64

存活区和eden区所占的比率:2:64

 

-XX:TargetSurvivorRatio

=50

该值是一个百分比,控制允许使用的生存区空间的比例,默认值是50。即占到50%,则执行Copy策略。该参数设置较大的话可提高对survivor空间的使用率。

 

-XX:ThreadStackSize

=512

每个线程栈大小(K),等于0时表示使用缺省值【Sparc: 512KSolaris Intel: 256KSparc 64bit: 1024其他的都为0

 

-XX:+UseBoundThreads

绑定用户级线程(Solaris only),这个选项强制所有的Java线程在创建时都作为操作系统绑定的线程

这个参数用来是否将JVM用户线程绑定到Solaris内核线程

-XX:+UseAltSigs

 

 

-XX:+UseV8InstrsOnly

 

 

-XX:-UseLWPSynchroniza

tion

使用操作系统提供的轻量级线程LWP同步来代替基于Java虚拟机的线程的同步

该参数的使用使得JVM将线程同步的控制交由Solaris内核处理,从而代替了JVM内部的线程同步机制

-XX:+UseThreadPriorities

设置是否使用本地线程优先级

 

-XX:-UseSpinning

 

 

-XX:+UseTLAB

是否使用线程本地对象分配策略,SUN Sparc –server时为true,其他为false

 

-XX:-UseISM

如果使用ISM选项可以获得如下的几个好处:1、使用大内存页来代替操作系统缺省的8K的页模式;

2、将一些内存页锁定在内存中,而不必换出到硬盘

 

如果系统使用ISM则系统文件/etc/system需要添加如下配置:

set shmsys:

shminfo_shmmax

=0xffffffff

set shmsys:

shminfo_shmseg=32

-XX:+UseFastAccessorMethods

原始类型的快速优化,get,set方法转成本地代码。

 

-XX:+UseBiasedLocking

锁机制的性能改善。

 


        1. JVM GC/Hotspot相关参数集


注:即时编译是Hotspot中的概念,按代收集,火车算法等是属于GC中的概念。


 


13.   项目调优经验1


  1. CMS垃圾收集器

    Date2010-12-24

    JVM参数:


-Xss2M -XX:PermSize=128M -XX:MaxPermSize=128M -Xms2G -Xmx2G -Xmn832M -XX:SurvivorRatio=6 -XX:TargetSurvivorRatio=80 -XX:ParallelGCThreads=8 -XX:+UseConcMarkSweepGC


观察分析:


  1. 新生代每次执行一次垃圾收集,旧生代增加一点:


旧生代:489,711 Kbà489,740Kb ;


新生代(638,976kb)651,878Kbà29,00kb


存活区(106,496kb):10,000kbà5,000kb


 


上述数据说明:对象在存活只存活一个周期,就被移到旧生代;相对应新生代每次收集存活区的最高利用率只有10%,根据上述参数可以把存活区Copy次数改为:(新生代分配值/存活区最大利用值)*目标存活区利用率=100M/10M*80%=8


 


  1. 垃圾收集数据

    新生代收集:ParNew29388次,用时:13m

    旧生代:ConurrentMarkSweep:0次,用时0s

     

    新生代平均每次收集暂停时间为:0.027s(平均每次用时:13m/29388=0.027s)。

    新生代收集29388次,旧生代0次:旧生代内存利用率远远低于新生代,可以适当放大旧生代,缩小新生代。

     

  2.  

     

    结论:扩大新生代内存,缩小旧生代内存,相应提升并发收集线程数量;增加-XX:MaxTenuringThreshold参数,设置值为8,要求对象在存活区之间Copy 10次在移动到旧生代。

     


  1. 并行垃圾收集自适应调整堆内存

     


**管控项目:64windows服务器


jvm 64: jdk1.5.0_16_b02


上线时间:2010.10,出现jvm不响应时间:2010.12.16


启动参数:


-server-Xss2M -XX:PermSize=64M -XX:MaxPermSize=64M


-XX:+UseParallelGC-XX:+UseAdaptiveSizePolicy -XX:+AggressiveHeap -XX:ParallelGCThreads=4


说明:采用并行垃圾回收器,堆内存通过JVM自适应调整(-XX:+UseAdaptiveSizePolicy-XX:+AggressiveHeap)。


 


问题:2010.12.16项目组发现运行在jvm上的引擎服务不响应,经分析是jvm在做垃圾全收集时,对jvm进行了一次自动重启。


 


方案:采用并发收集器,内存参数固定”,手动执行垃圾收集后,不出现jvm重启现象。


 


  1. 采用并发收集,内存固定


 


**管控项目:64windows服务器


jvm 64: jdk1.5.0_16_b02


 


启动参数:


-server-Xss2M -XX:PermSize=64M -XX:MaxPermSize=64M -Xms2G -Xmx2G -Xmn512M


-XX:SurvivorRatio=16-XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=10 -XX:ParallelGCThreads=4


 


分析:旧生代采用并发垃圾收集器(CMS)管理;新生代采用并行垃圾收集器(Parall);内存参数固定。


 


正常运行时间

1 hour 4 minutes

处理 CPU时间

6 minutes

JIT编译器

HotSpot 64-Bit Server Compiler

编译总时间

1 minute


 


垃圾收集器

Name = 'ParNew', Collections = 158, Total time spent = 9.392

垃圾收集器

Name = 'ConcurrentMarkSweep', Collections = 1, Total time spent = 2.076


 


 


 


14.   调优经验(摘抄)


14.1.  GC调优参数的使用


JVM中按代收集算法的基本原则是这样的,JVM堆空间被分成许多子堆,每个子堆用于存放不同代的对象,而当所有已经存在的堆中的各代对象都不能继续回收时,则新的子堆会被分配,用于存放新一代的对象,下面的这两个参数就是为按代收集设计的:


-XX:NewRatio=2              //新生代于老一代的空间比率


-XX:NewSize=2228224           //缺省时2K,对于应用服务器系统建议调整的大一些4K8K


-XX:MaxNewSize                    //新生代的最大空间


-XX:MaxPermSize=64m         //老一代的最大空间,缺省为64M,建议增加


-XX:SurvivorRatio                  //GC时代子堆中的年老对象的比率


-Xxincgc                           //是否在垃圾收集时启用火车算法


-XX:+UseConcMarkSweepGC//是否启用并发收集算法


-Xverifyheap              //仅仅用于Debug版本,用于对GC数据分析


-XX:TargetSurvivorRatio=50   //GC收集后期望得到的被老一代占用的空间,建议不调整


 


因为垃圾收集只是在各代的子堆满了的时候发生,总的堆的空间状况也会对垃圾收集产生重要的影响,JVM向操作系统申请更多的堆内存空间的前提是,堆中所有的年老的代的子堆都已经满了。


-Xms                          //设置最小初始堆空间的大小


-Xmx                          //设置最大堆空间的大小


-XX:MinFreeHeapRatio=40     //GCJVM堆空间向操作系统缩小的比率。


-XX:MaxHeapFreeRatio70  //GCJVM堆空间向操作系统扩张的比率。


-XX:+AggressiveHeap            //用于JVM运行于大内存模式下,JVM的堆空间至少在1G以上,与-Xms-Xmx不同时使用,慎用!会导致JVM极度消/耗内存


U-NICA这样的后台大型应用服务器系统来说,我们就采用了通过调整初始堆内存空间、堆增长量、增加新生代空间配置、使用并发收集算法、火车算法等方法来使的系统的垃圾收集能力得以优化。而且在具体实际测试过程中也发现这些调整通常是有效而且成本低廉的。


14.2.  JIT调优参数的使用


从解释执行到即时编译,再到热点编译JVM走在一条不断优化的道路上,今天我们通过一些简单的参数设置就可以获得以前梦想的性能,对于Java用户来说,这的确是一条捷径:


-server                       //很多Hotspot的能力都是通过这个选项打开的,对于大型服


//务器尤为重要,这里可以启动热点编译功能


-Xmaxjitcodesize32m       //设置即时编译代码的最大尺寸


-Xint                           //纯解释执行,一般情况下不用它


-Xtime                        //不太清楚如何使用,总之是指定JIT的时间


-XX:+DisableExplicitGC          //是否屏蔽应用层的垃圾收集请求


-XX:-UseISM                    //使用大内存页模式则会减少GC的时间


-XX:-UseMPSS                //在使用ISM选项的同时不使用该选项,否则ISM无效


14.3.  Java线程调优参数的使用


-XX:-UseBoundThreads          //如果你的系统是Solaris8以上,尽情使用这个参数吧,操作系统内核的线程调度总是要必应用层的调度快一些


-XX:-UseLWPSynchronization//这个参数也是同样的,让操作系统来做线程同步这些工作


-XX:+UseThreadPriorities       //是否采用操作系统内部定义的线程优先级


-XX:CompileThreshold=10000//如果你想让你的系统更早变快一些,而且你的内存足够多的话,可以将这个参数值调小


-XX:PreBlockSpin=10             //仅仅用于Linux版本


-XX:ThreadStackSize=512     //设置线程栈的大小,如果你的应用中有比较大的循环或递


归时使用。


-XX:+UseTLAB                //是否在线程的栈空间内分配对象,如果你的内存较大的话,并且配置了比较大的线程栈空间,则使用这个参数会使得临时&本地对象的申请和释放比较快。



版权声明:本文为博主原创文章,未经博主允许不得转载。 http

JVM性能调优1:JVM性能调优理论及实践(收集整理)

本系列包括:


注:本文部分内容收集整理了网上的资料。

1.      内存结构


1.1.     分代结构图


注意:


JVM中,非堆内存,根据模式不同分为不同的几个部分。


-Server下:非堆包括:持久代和代码缓存(Code cache


-client下:非堆包括:持久代、代码缓存(Code cache)、Perm rwperm ro


2.      性能指标


对于垃圾收集的性能,主要有两个指标。


吞吐量throughput)指用来进行垃圾收集之外工作所用的时间占总时间的百分比,一般要通过长时间的观察和测量。吞吐量包括了分配内存所花费的时间在内(一般来说无需对分配进行调优)。


Throughput is the percentage of total time not spent in garbage collection,considered over long periods of time. Throughput includes time spent inallocation (but tuning for speed of allocation is generally not needed.) fromTuning GC with jdk 1.5


暂停Pause)指由于进行垃圾收集而导致应用无法响应的时间。


Pauses are the times when an application appears unresponsive becausegarbage collection is occurring.


Footprint is the working set of a process, measured in pages and cachelines. On systems with limited physical memory or many processes, footprint maydictate scalability.      Promptnessis the time between when an object becomes dead and when the memory becomesavailable, an important consideration for distributed systems, including remotemethod invocation (RMI). fromTuning GC with jdk 1.5


用户对于垃圾收集有不同的需求,例如,对于Web服务器应用来说,吞吐量是要着重考虑的,而暂停时间可能由于网络的反应时间而不那么明显;而对于一个交互式图形界面的应用来说,即使是短暂的暂停都会带来非常不好的用户体验。


In general, aparticular generation sizing chooses a trade-off between these considerations.For example, a very largeyoung generation may maximize throughput,but does so at the expense of footprint, promptness, and pause times.younggeneration pauses can be minimized by using a smallyoung generationat the expense of throughput. To a first approximation, the sizing of onegeneration does not affect the collection frequency and pause times for anothergeneration .fromTuning GC with jdk 1.5


通常来说,如何设置代的大小是在这些考虑因素之间作出的一个权衡。例如,将新生代设置得很大会得到很好的吞吐性能,但是会增加暂停时间;反之,较小的新生代设置会减小暂停时间,但是降低了吞吐量。


一个代的大小不应该影响在其他代上进行垃圾收集的频率和暂停时间。


3.      引用类型


对象引用类型分为强引用、软引用、弱引用和虚引用


3.1.     强引用


就是我们一般声明对象是时虚拟机生成的引用,强引用环境下,垃圾回收时需要严格判断当前对象是否被强引用,如果被强引用,则不会被垃圾回收。


3.2.     软引用


软引用一般被做为缓存来使用。与强引用的区别是,软引用在垃圾回收时,虚拟机会根据当前系统的剩余内存来决定是否对软引用进行回收。如果剩余内存比较紧张,则虚拟机会回收软引用所引用的空间;如果剩余内存相对富裕,则不会进行回收。换句话说,虚拟机在发生OutOfMemory时,肯定是没有软引用存在的。


3.3.     弱引用


弱引用与软引用类似,都是作为缓存来使用。但与软引用不同,弱引用在进行垃圾回收时,是一定会被回收掉的,因此其生命周期只存在于一个垃圾回收周期内。


3.4.     虚引用


 


4.      内存分配回收次序


新生代是类的诞生、成长、消亡的区域,一个类在这里产生,应用,最后被垃圾回收器收集,结束生命。


新生区又分为两部分:伊甸区(Eden space)和幸存者区(Survivorpace),所有的类都是在endennew出来的。幸存区有两个: 0区(Survivor 0 space)和1区(Survivor 1 space)。


Eden的空间用完时,程序又需要创建对象,JVM的垃圾回收器将对enden区进行垃圾回收,将enden区中的不再被其他对象所引用的对象进行销毁(次收集)。


然后将伊甸园中的剩余对象移动到幸存0区。若幸存0区也满了,再对该区进行垃圾回收(次收集),然后移动到1区。那如果1区也满了呢?(次收集)


再移动到养老区。


 



新生代采用复制算法,old区采用标记清除算法。


无论是复制算法还是标记清除算法,在垃圾收集期间都要暂停客户的应用程序(cms垃圾收集器除外,它在初始标记和重新标记时暂停,并发标记和并发清除时,客户线程不暂停)。


无论是复制算法还是标记清除算法,在最开始,倒要标记活动的对象,即对象的可达性分析,在这里,必须要求对象是一致的(即可达性分析期间,内存状态是冻结的);在cms收集器是,在初始标记(对root对象标记时,要求冻结)冻结,但此时会产生浮动垃圾,即在并发标记时,由分配了新的对象(因为没有冻结)。


综述,基本过程是,复制算法:标记(暂停)-->复制(暂停,因对象地址发生变化);标记清除整理算法:标记(暂停)-->清除整理(消除碎片,暂停,因对象地址发生变化)。


root对象标记很快,对内存扫描分析,可达性分析过程很慢。清除很慢(内存回收),内存整理、复制(慢)。


内存分配需要时间。


5.      对象标记算法(Object Marking Algorithms


5.1.     引用计数法(ReferenceCounting


堆中每一个对象都有一个引用计数。当新创建一个对象,或者有变量被赋值为这个对象的引用,则这个对象的引用计数加1;当一个对象的引用超过生存期或者被设置一个新的值时,这个对象的引用计数减1。当对象的引用计数变为0时,就可以被当作垃圾收集。


这种方法的好处是垃圾收集较快,适用于实时环境。缺点是这种方法无法监测出循环引用。例如对象A引用对象B,对象B也引用对象A,则这两个对象可能无法被垃圾收集器收集。因此这种方法是垃圾收集的早期策略,现在很少使用。


5.2.     根搜索算法(Garbage Collection Roots Tracing


5.2.1.    基本思想


这种方法把每个对象看作图中一个节点,对象之间的引用关系为图中各节点的邻接关系。垃圾收集器从一个或数个根结点遍历对象图,如果有些对象节点永远无法到达,则这个对象可以被当作垃圾回收。


容易发现,这种方法可以检测出循环引用,避免了引用计数法的缺点,较为常用。步骤如下:


  1. 选定一些对象,作为 GC Roots,组成基对象集;

  2. 由基对象集内的对象出发,搜索所有可达的对象;

  3. 其余的不可达的对象,就是可以被回收的对象。

    这里的可达不可达与图论中的定义一样,所有的对象被看做点,引用被看做有向连接,整个引用关系就是一个有向图。在引用计数法中提到的循环引用,其实就是有向图中有环的情况,即构成有向有环图。引用计数法不适用于有向有环图,而根搜索算法适用于所有有向图,包括有环的和无环的。


5.2.2.    GCRoots


如果你的逻辑思维够清晰,你会说一定与选取基对象集的方法有关。是的,没错。选取 GC Roots 组成基对象集,其实就是选取如下这些对象:


《深入理解 Java 虚拟机:JVM高级特性与最佳实践》一书中提到的 GC Roots 为:


1.     方法区(Method Area,即 Non-Heap)中的类的 static 成员引用的对象,和 final成员引用的对象;


2.     Java方法栈(Java Method Stack)的局部变量表(Local Variable Table)中引用的对象;


3.     原生方法栈(Native Method Stack)中 JNI中引用的对象。


但显然不够全面,[参考2]中提到的要更全面:(March 6th,2012 update


1.     由系统类加载器加载的类相应的对象:这些类永远不会被卸载,且这些类创建的对象都是 static 的。注意用户使用的类加载器加载的类创建的对象,不属于 GC Roots,除非是 java.lang.Class 的相应实例有可能会称为其他类的 GC Roots


2.     正在运行的线程。


3.     Java方法栈(Java Method Stack)的局部变量表(Local Variable Table)中引用的对象。


4.     原生方法栈(Native Method Stack)的局部变量表(Local Variable Table)中引用的对象。


5.     JNI中引用的对象。


6.     同步监控器使用的对象。


7.      JVM  GC控制的对象:这些对象是用于 JVM内部的,是实现相关的。一般情况下,可能包括系统类加载器(注意与“1”不一样,“1”中是 objects created by the classes loaded bysystem class loaders,这里是 theobjects, corresponding instances of system class loaders)、JVM内部的一些重要的异常类的对象、异常句柄的预分配对象和在类加载过程中自定义的类加载器。不幸的是,JVM并不提供这些对象的任何额外的详细信息。因此这些实现相关的内容,需要依靠分析来判定。


所以这个算法实施起来有两部分,第一部分就是到 JVM 的几个内存区域中找对象,第二部分就是运用图论算法


 


 


 


 


6.      垃圾回收算法


6.1.     标记-清除(Mark-Sweep



此算法执行分两阶段。第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除。此算法需要暂停整个应用,同时,会产生内存碎片。


6.2.     标记-整理(Mark-Compact


此算法结合了标记-清除复制两个算法的优点。也是分两阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,把清除未标记对象并且把存活对象压缩到堆的其中一块,按顺序排放。此算法避免了标记-清除的碎片问题,同时也避免了复制算法的空间问题。


6.3.     复制(Copying


此算法把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中。次算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理,不会出现碎片问题。当然,此算法的缺点也是很明显的,就是需要两倍内存空间


6.4.     增量收集算法


增量收集器把堆栈分为多个域,每次仅从一个域收集垃圾。这会造成较小的应用程序中断。


6.5.     分代收集算法


这种收集器把堆栈分为两个或多个域,用以存放不同寿命的对象。虚拟机生成的新对象一般放在其中的某个域中。过一段时间,继续存在的对象将获得使用期并转入更长寿命的域中。分代收集器对不同的域使用不同的算法以优化性能。这样可以减少复制对象的时间。


6.6.     并发收集算法


并发收集器与应用程序同时运行。这些收集器在某点上(比如压缩时)一般都不得不停止其他操作以完成特定的任务,但是因为其他应用程序可进行其他的后台操作,所以中断其他处理的实际时间大大降低。


6.7.     并行收集器


并行收集器使用某种传统的算法并使用多线程并行的执行它们的工作。在多CPU机器上使用多线程技术可以显著的提高java应用程序的可扩展性。


6.8.     自适应收集器


根据程序运行状况以及堆的使用状况,自动选一种合适的垃圾回收算法。这样可以不局限与一种垃圾回收算法。


6.9.     火车增量算法


垃圾收集算法一个很大的缺点就是难以控制垃圾回收所占用的CPU时间,以及何时需要进行垃圾回收。火车算法是分代收集器所用的算法,目的是在成熟对象空间中提供限定时间的渐进收集。目前应用于SUN公司的Hotspot虚拟机上。


在火车算法中,内存被分为块,多个块组成一个集合。为了形象化,一节车厢代表一个块,一列火车代表一个集合,见图一


图一


注意每个车厢大小相等,但每个火车包含的车厢数不一定相等。垃圾收集以车厢为单位,收集顺序依次为1.11.21.31.42.12.22.33.13.23.3。这个顺序也是块被创建的先后顺序。


垃圾收集器先从块1.1开始扫描直到1.4,如果火车1四个块中的所有对象没有被火车2和火车3的对象引用,而只有火车1内部的对象相互引用,则整个火车1都是垃圾,可以被回收。


如图二,车厢1.1中有对象A和对象B1.3中有对象C1.4中有对象D,车厢2.2中有对象E,车厢3.3中有对象F。在火车1中,对象C引用对象A,对象B引用对象D,可见,火车2和火车3没有引用火车1的对象,则整个火车1都是垃圾。


图二


如果火车1中有对象被其它火车引用,见图三,扫描车厢1.1时发现对象A被火车2中的E引用,则将对象A从车厢1.1转移到车厢2.2,然后扫描A引用的对象D,把D也转移到车厢2.2,然后扫描D,看D是否引用其它对象,如果引用了其它对象则也要转移,依次类推。扫描完火车1的所有对象后,剩下的没有转移的对象都是垃圾,可以把整个火车1都作为垃圾回收。注意如果在转移时,如果车厢2.2空间满了,则要在火车2末尾开辟新的车厢2.4,将新转移的对象都放到2.4,即火车的尾部)


图三


补充说明:垃圾回收器一次只扫描一个车厢。图三中的对象BC并不是立即被回收,而是先会被转移到火车1的尾部车厢。即扫描完1.1后,B被转移到火车1尾部,扫描完1.3后,C被转移到车尾。等垃圾收集器扫描到火车1尾部时,如果仍然没有外部对象引用它们,则BC会被收集。


火车算法最大的好处是它可以保证大的循环结构可以被完全收集,因为成为垃圾的循环结构中的对象,无论多大,都会被移入同一列火车,最终一起被收集。还有一个好处是这种算法在大多数情况下可以保证一次垃圾收集所耗时间在一定限度之内,因为一次垃圾回收只收集一个车厢,而车厢的大小是有限度的。


 


 


7.      触发垃圾收集的条件


由于对象进行了分代处理,因此垃圾回收区域、时间也不一样。GC有两种类型:Scavenge GCFull GC


7.1.     次收集ScavengeGC


一般情况下,当Enden区对象已满,或当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区进行,不会影响到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden去能尽快空闲出来。


Survivor区,当达到设置的预定值(默认50%),则进行一次垃圾次收集。


7.2.     全收集FullGC


对整个堆进行整理,包括YoungTenuredPermFull GC因为需要对整个对进行回收,所以比Scavenge GC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分工作就是对于FullGC的调节。有如下原因可能导致Full GC


·年老代(Tenured)被写满


·持久代(Perm)被写满


· System.gc()被显示调用


·上一次GC之后Heap的各域分配策略动态变化


8.      垃圾收集器类型


8.1.     串行收集器


用单线程处理所有垃圾回收工作,因为无需多线程交互,所以效率比较高。但是,也无法使用多处理器的优势,所以此收集器适合单处理器机器。当然,此收集器也可以用在小数据量(100M左右)情况下的多处理器机器上。可以使用-XX:+UseSerialGC打开。


8.2.     并行收集器


注:图有问题,是并发垃圾收集器的图。


并行垃圾回收,因此可以减少垃圾回收时间。一般在多线程多处理器机器上使用。


8.3.     并发垃圾收集


并发收集器主要减少年老代的暂停时间,他在应用不停止的情况下使用独立的垃圾回收线程,跟踪可达对象。在每个年老代垃圾回收周期中,在收集初期并发收集器会对整个应用进行简短的暂停,在收集中还会再暂停一次。第二次暂停会比第一次稍长,在此过程中多个线程同时进行垃圾回收工作。


浮动垃圾:由于在应用运行的同时进行垃圾回收,所以有些垃圾可能在垃圾回收进行完成时产生,这样就造成了“Floating Garbage”,这些垃圾需要在下次垃圾回收周期时才能回收掉。所以,并发收集器一般需要20%的预留空间用于这些浮动垃圾。


Concurrent Mode Failure:并发收集器在应用运行时进行收集,所以需要保证堆在垃圾回收的这段时间有足够的空间供程序使用,否则,垃圾回收还未完成,堆空间先满了。这种情况下将会发生并发模式失败,此时整个应用将会暂停,进行垃圾回收。


启动并发收集器:因为并发收集在应用运行时进行收集,所以必须保证收集完成之前有足够的内存空间供程序使用,否则会出现“Concurrent Mode Failure”。通过设置-XX:CMSInitiatingOccupancyFraction=<N>指定old区达到百分之多少时开始执行并发收集。


9.      垃圾收集器介绍


9.1.     串行垃圾收集


Serial Collector是指任何时刻都只有一个线程进行垃圾收集,这种策略有一个名字“stop the whole world",它需要停止整个应用的执行。这种类型的收集器适合于单器CPU的机器。


Serial Copying Collector


此种GC-XX:UseSerialGC选项配置,它只用于新生代对象的收集。1.5.0以后.


-XX:MaxTenuringThreshold来设置对象复制的次数。当eden空间不够的时候,GC会将eden的活跃对象和一个名叫From survivor空间中尚不够资格放入Old代的对象复制到另外一个名字叫To Survivor的空间。而此参数就是用来说明到底From survivor中的哪些对象不够资格,假如这个参数设置为31,那么也就是说只有对象复制31次以后才算是有资格的对象。


这里需要注意几个个问题:


  • From SurvivorTo survivor的角色是不断的变化的,同一时间只有一块空间处于使用状态,这个空间就叫做From Survivor区,当复制一次后角色就发生了变化。

  • 如果复制的过程中发现To survivor空间已经满了,那么就直接复制到old generation.

  • 比较大的对象也会直接复制到Old generation,在开发中,我们应该尽量避免这种情况的发生。

     

     

    并行垃圾收集(Parallel Collector

    Parallel Copying Collector

    -XX:+UseParNewGC用来设置年轻代为并发收集【多CPU】,如果你的服务器有多个CPU,你可以开启此参数;开启此参数,多个CPU可并发进行垃圾回收,可提高垃圾回收的速度。此参数和-XX:ParallelGCThreads搭配使用。适用于1.4以后版本。


Parallel Mark-Compact Collector


此种GC-XX:+UseParallelOldGC参数配置,此GC主要用于老生代对象的收集。适用于1.6.0以后版本。


Parallel scavenging Collector


-XX:+UseParallelGC它是对新生代对象的垃圾收集器。年轻代使用并行收集,而年老代仍旧使用串行收集。可提高系统的吞吐量。此参数和-XX:ParallelGCThreads搭配使用。适用于1.4以后版本。它比较适合于对吞吐量高于暂停时间的场合。


 


采用多线程并行(Parallel)进行垃圾收集。


9.3.     并发垃圾收集器(Concurrent Collector)


此种GC可以用参数-XX:+UseConcMarkSweepGC配置,此GC主要用于老生代和Perm代的收集。


Concurrent Collector通过并发的方式进行垃圾收集,这样就减少了垃圾收集器收集一次的时间,这种GC在实时性要求高于吞吐量的时候比较有用。


在应用运行的同时,并发(Concurrent)进行垃圾收集。


  1. 配置垃圾回收线程数量

             -XX:ParallelGCThreads年轻代并行垃圾收集的前提下(对并发也有效果)的线程数,增加并行度,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。
    例如:-XX:ParallelGCThreads=2

  2. 配置垃圾收集器需要注意的地方

    垃圾收集器不可重复配置,例如已经配置了新生代并行收集(-XX:+UseParNewGC),此时不能配置任何对新生代的垃圾收集。可以配置对旧生代和持久对象的垃圾收集。

    例如:

    -XX:+UseParNewGC-XX:+UseConcMarkSweepGC -XX:ParallelGCThreads=2

    可组合适用。

    增量

    配合并发垃圾收集,适用于单cpu的架构,sun不建议使用。

     

    自适应

    自适应垃圾收集,运行在并行垃圾收集上。在并行收集时对于收集时间、分配比例、收集之堆的空闲空间等数据进行统计分析,然后以此为依据调整新生代和旧生代的大小以达到最佳效果。

     

     

    小结

             J2SE平台1.4.2版本开始,加入了另外3种垃圾收集器,都是着重提高吞吐能力,降低垃圾收集时的暂停时间。

             1. 吞吐收集器(throughput collector):命令行参数:-XX:+UseParallelGC。在新生代使用并行收集策略,在旧生代和默认收集器相同。

             吞吐收集器和默认的收集器类似,都是分代收集器。不同之处就在于吞吐收集器用多线程进行次要收集,

            J2SE平台1.4.1版本开始,吞吐收集器就具有了一个特征,就是大小自适应(参数-XX:

    +UseAdaptiveSizePolicy),这个选项默认是打开的。该特征对于收集时间、分配比例、收集之   堆的空闲空间等数据进行统计分析,然后以此为依据调整新生代和旧生代的大小以达到最佳效果。可以使用-verbose:gc来查看堆的大小。

             -XX:+AggressiveHeap选项会检测主机的资源(内存大小、处理器数量),然后调整相关的参数,使得长时间运行的、内存申请密集的任务能够以最佳状态运行。该选项最初是为拥有大量内存和很多处理器的主机而设计的,但是从J2SE1.4.1以及其后继版本来看,即使是对于那些只有4CPU       主机,该选项都是很有帮助的。因此,吞吐收集器(-XX:+UseParallelGC)、大小自适应 -XX:+UseAdaptiveSizePolicy)以及本选项(-XX:+AggressiveHeap)经常结合在一起使用。要使    用本选项,主机上至少要有256M的物理内存,堆内存的最初大小是基于物理内存计算出来的,然后 会根据需要尽可能的利用物理内存。

           2. 并发收集器(concurrent low pause collector):命令行参数:-XX:+UseConcMarkSweepGC。在旧生代使用并发收集策略,大部分收集工作都是和应用并发进行的,在进行收集的时候,应用的暂停时间很短。如果综合使用-XX:+UseParNewGC-XX:+UseConcMarkSweepGC,那么在新生代上使用并行的收集策略。

             3. 增量收集器(incremental low pause collector):命令行参数:-Xincgc。使用增量收集器要谨慎,他只是在每次进行次要收集的时候对旧生代进行一部分的收集,这样就把主要收集所带来的较长时间的停顿分散到多次的次要收集。但是,考虑到总共的吞吐,可能比旧生代上默认的收集还要慢。

    注意,-XX:+UseParallelGCXX:+UseConcMarkSweepGC不能同时使用。对于J2SE1.4.2版本会检查垃圾收集相关参数组合的合法性,但是对于之前的版本没有这个检查,可能会导致不可预知的错误。

    垃圾回收器相关参数

    串行垃圾收集器参数

     


参数

说明

-XX:+UseSerialGC

设置串行收集器

 

 


 


10.2.  并行垃圾收集器参数


参数

说明

-XX:+UseParallelGC

选择垃圾收集器为并行收集器,此配置仅对年轻代有效,即上述配置下,年轻代使用并行收集,而老年代仍旧使用串行收集。采用了多线程并行管理和回收垃圾对象,提高了回收效率,提高了服务器的吞吐量,适合于多处理器的服务器。

-XX:ParallelGCThreads

配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。

-XX:+UseParallelOldGC

采用对于老年代并发收集的策略,可以提高收集效率。JDK6.0支持对老年代并行收集。

-XX:MaxGCPauseMillis

设置每次年轻代并行收集最大暂停时间,如果无法满足此时间,JVM会自动调整年轻代大小以满足此值。

-XX:+UseAdaptiveSizePolicy

设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低响应时间或者收集频率等,此值建议使用并行收集器时,一直打开。

-XX:+UseParNewGC

指定在 New Generation使用 parallel collector, UseParallelGC gc的升级版本 ,有更好的性能或者优点 ,可以和 CMS gc一起使用


 


 


10.3.  并发垃圾收集器参数


参数

说明

-XX:+UseConcMarkSweepGC

指定在老年代使用 concurrent cmark sweep gcgc thread app thread并行 ( init-mark remark pause app thread)app pause时间较短 ,适合交互性强的系统 , web server。它可以并发执行收集操作,降低应用停止时间,同时它也是并行处理模式,可以有效地利用多处理器的系统的多进程处理。

-XX:+UseCMSCompactAtFullCollection

打开对老年代的压缩。可能会影响性能,但是可以消除碎片,FULL GC的时候,压缩内存, CMS是不会移动内存的,因此,这个非常容易产生碎片,导致内存不够用,因此,内存的压缩这个时候就会被启用。增加这个参数是个好习惯。

-XX:+CMSIncrementalMode

设置为增量模式。适用于单CPU情况

-XX:CMSFullGCsBeforeCompaction

由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生碎片,使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。

-XX:+CMSClassUnloadingEnabled

使CMS收集持久代的类,而不是fullgc

-XX:+CMSPermGenSweepingEnabled

使CMS收集持久代的类,而不是fullgc

-XX:-CMSParallelRemarkEnabled

在使用 UseParNewGC的情况下 ,尽量减少 mark的时间。

-XX:CMSInitiatingOccupancyFraction

说明老年代到百分之多少满的时候开始执行对老年代的并发垃圾回收(CMS),这个参数设置有很大技巧,基本上满足公式:

(Xmx-Xmn)*(100-CMSInitiatingOccupancyFraction)/100>=Xmn

时就不会出现promotion failed。在我的应用中Xmx6000Xmn500,那么Xmx-Xmn5500兆,也就是老年代有5500兆,CMSInitiatingOccupancyFraction=90说明老年代到90%满的时候开始执行对老年代的并发垃圾回收(CMS),这时还剩10%的空间是5500*10%=550兆,所以即使Xmn(也就是年轻代共500兆)里所有对象都搬到老年代里,550兆的空间也足够了,所以只要满足上面的公式,就不会出现垃圾回收时的promotion failed

如果按照Xmx=2048,Xmn=768的比例计算,则CMSInitiatingOccupancyFraction的值不能超过40,否则就容易出现垃圾回收时的promotion failed

-XX:+UseCMSInitiatingOccupancyOnly

指示只有在老年代在使用了初始化的比例后 concurrent collector 启动收集


 


11.   JVM内存溢出类型


对于JVM的内存写过的文章已经有点多了,而且有点烂了,不过说那么多大多数在解决OOM的情况,于此,本文就只阐述这个内容,携带一些分析和理解和部分扩展内容,也就是JVM宕机中的一些问题,OK,下面说下OOM的常见情况:


第一类内存溢出,就是堆栈溢出:关键字:java.lang.OutOfMemoryError: ......javaheap space.....


也就是当你看到heap相关的时候就肯定是堆栈溢出了,此时如果代码没有问题的情况下,适当调整-Xmx-Xms是可以避免的,不过一定是代码没有问题的前提,为什么会溢出呢,要么代码有问题,要么访问量太多并且每个访问的时间太长或者数据太多,导致数据释放不掉,因为垃圾回收器是要找到那些是垃圾才能回收,这里它不会认为这些东西是垃圾,自然不会去回收了;主意这个溢出之前,可能系统会提前先报错关键字为:


java.lang.OutOfMemoryError:GC over headlimit exceeded


这种情况是当系统处于高频的GC状态,而且回收的效果依然不佳的情况,就会开始报这个错误,这种情况一般是产生了很多不可以被释放的对象,有可能是引用使用不当导致,或申请大对象导致,但是java heap space的内存溢出有可能提前不会报这个错误,也就是可能内存就直接不够导致,而不是高频GC.


第二类内存溢出,PermGen的溢出,或者PermGen满了的提示,你会看到这样的关键字:关键信息为:java.lang.OutOfMemoryError: PermGenspace


原因:系统的代码非常多或引用的第三方包非常多、或代码中使用了大量的常量、或通过intern注入常量、或者通过动态代码加载等方法,导致常量池的膨胀,虽然JDK 1.5以后可以通过设置对永久带进行回收,但是我们希望的是这个地方是不做GC的,它够用就行,所以一般情况下今年少做类似的操作,所以在面对这种情况常用的手段是:增加-XX:PermSize-XX:MaxPermSize的大小。


第三类内存溢出:在使用ByteBuffer中的allocateDirect()的时候会用到,很多javaNIO的框架中被封装为其他的方法溢出关键字:java.lang.OutOfMemoryError: Directbuffer memory


如果你在直接或间接使用了ByteBuffer中的allocateDirect方法的时候,而不做clear的时候就会出现类似的问题,常规的引用程序IO输出存在一个内核态与用户态的转换过程,也就是对应直接内存与非直接内存,如果常规的应用程序你要将一个文件的内容输出到客户端需要通过OS的直接内存转换拷贝到程序的非直接内存(也就是heap中),然后再输出到直接内存由操作系统发送出去,而直接内存就是由OS和应用程序共同管理的,而非直接内存可以直接由应用程序自己控制的内存,jvm垃圾回收不会回收掉直接内存这部分的内存,所以要注意了哦。


如果经常有类似的操作,可以考虑设置参数:-XX:MaxDirectMemorySize


第四类内存溢出错误:溢出关键字:java.lang.StackOverflowError


这个参数直接说明一个内容,就是-Xss太小了,我们申请很多局部调用的栈针等内容是存放在用户当前所持有的线程中的,线程在jdk 1.4以前默认是256K1.5以后是1M,如果报这个错,只能说明-Xss设置得太小,当然有些厂商的JVM不是这个参数,本文仅仅针对Hotspot VM而已;不过在有必要的情况下可以对系统做一些优化,使得-Xss的值是可用的。


第五类内存溢出错误:溢出关键字:java.lang.OutOfMemoryError: unable tocreate new native thread


上面第四种溢出错误,已经说明了线程的内存空间,其实线程基本只占用heap以外的内存区域,也就是这个错误说明除了heap以外的区域,无法为线程分配一块内存区域了,这个要么是内存本身就不够,要么heap的空间设置得太大了,导致了剩余的内存已经不多了,而由于线程本身要占用内存,所以就不够用了,说明了原因,如何去修改,不用我多说,你懂的。


第六类内存溢出:溢出关键字java.lang.OutOfMemoryError: request {}byte for {}out of swap


这类错误一般是由于地址空间不够而导致。


六大类常见溢出已经说明JVM99%的溢出情况,要逃出这些溢出情况非常困难,除非一些很怪异的故障问题会发生,比如由于物理内存的硬件问题,导致了code cache的错误(在由byte code转换为native code的过程中出现,但是概率极低),这种情况内存会被直接crash掉,类似还有swap的频繁交互在部分系统中会导致系统直接被crash掉,OS地址空间不够的话,系统根本无法启动,呵呵;JNI的滥用也会导致一些本地内存无法释放的问题,所以尽量避开JNIsocket连接数据打开过多的socket也会报类似:IOException: Too many open files等错误信息。


JNI就不用多说了,尽量少用,除非你的代码太牛B了,我无话可说,呵呵,这种内存如果没有在被调用的语言内部将内存释放掉(如C语言),那么在进程结束前这些内存永远释放不掉,解决办法只有一个就是将进程kill掉。


另外GC本身是需要内存空间的,因为在运算和中间数据转换过程中都需要有内存,所以你要保证GC的时候有足够的内存哦,如果没有的话GC的过程将会非常的缓慢。


 


12.   JAVA虚拟机参数分类说明


12.1.  标准参数-


所谓的Java虚拟机标准参数指的就是所有的虚拟机实现都应该支持的参数,这部分参数基本上都是对虚拟机基本能力的调整,包括对运行模式、垃圾收集信息显示、显示版本信息、断言开关等,下面是以Solaris1.4.2版本为例:


参数

使用说明

备注

-d32

-d64

指明该Java VM是运行与32位环境还是64位环境,默认是运行在32位环境下的,如果是配置了64位模式则需要操作系统也必须是64位的,当然CPU更需要是64位的。另外如果我们选择了-server参数,则就暗含了64位模式。

因为64CPU兼容32位操作系统,而64位操作系统又是兼容32位执行程序

-client

-server

设置该JVM运行与Client或者Server Hotspot模式,这两种模式从本质上来说是在JVM中运行不同的JIT(运行时编译模块)代码,并且两者在JVM内部的接口是一致的。客户端模式优化的是系统启动时间更快,而服务端模式的优化则更关注与系统的整体性能。一般来说Client选项用于GUI的应用,Server选项多用于后台服务器应用。

另外两者在编译策略、垃圾收集策略、堆使用上也有所不同

-hotspot

Hotspot类型的JVM中缺省使用,缺省为Client Hotspot模式。

 

-cp

-classpath

指明JVM启动时要加载的类文件路径,Java虚拟机进程在启动时就会按照该参数后面指明的路径查找*.zip*.jar*.class文件,然后将这些包中的类文件加载到内存中。

JVM加载类文件的顺序是

-D<name>=<value>

设置系统属性的值,该参数是的设计是为了满足Java应用程序员与JVM进行参数传递的手段之一,另一种是通过应用级参数(argument)来实现。

Java程序员可以在程序内调用system.getProperty来获取用户通过-D参数传进来的系统属性信息。而命令行参数就是是JVM传递给main函数的调用参数

 

-verbose:class

 

-verbose:gc

 

-verbose:jni

打印详细信息,目前支持打印类加载信息:class、垃圾收集信息:gc、以及本地方法调用信息:jni,如果选择了此选项,则JVM会在命令行打印出上述信息;

对于测试中的系统可以通过打开:gc开关,查看JVM每次垃圾收集的详细信息来判断系统内存消耗情况,如果系统垃圾收集的很频繁,而且每次都回收了大量的内存,则说明系统内存消耗很大,对象的创建和湮灭很频繁,而如果堆内存一直保持着增长的话,说明可能存在内存“泄漏”。

-version

-showversion

-version选项是显示版本信息后JVM退出

-showversion选项是显示版本信息后JVM继续运行

 

-esa

-enableassertions

打开系统中每个类的断言开关

该选项用于程序开发、调试过程

-da

-disableassertions

关闭系统中每个类的断言开关

该选项用于程序开发、调试过程

 

 

 


        1. JVM标准参数集


12.2.  扩展参数-X


所谓的Java虚拟机非标准参数指的就是一些特有的虚拟机实现所支持,下面以Solaris1.4.2版本为例介绍一些扩展的虚拟机运行参数,其中对Hotspot VM相关的参数是我们进行性能调整的重点。


参数

使用说明

备注

-Xmixed

JVM执行模式的设置参数,混合模式即支持Hotspot即时编译的运行模式

支持HotspotJVM缺省都是运行于混合模式的。

-Xint

设置JVM的执行模式为解释执行模式,纯解释执行的JVM对多数应用来说基本上时没有意义的,仅仅可能会在一些嵌入式系统中应用

 

-Xbootclasspath

设置初始类装载器的装载路径

 

-Xnoclassgc

设置不执行类垃圾收集

 

-Xincgc

设置是否启动火车垃圾收集算法

 

-Xloggc:<file>

设置是否将GC信息写入日志文件

 

-Xbatch

设置不执行后台编译

 

-Xms<size>

设置JVM启动时初始内存堆的大小

 

-Xmx<size>

设置JVM启动后动态申请堆内存的最大堆空间

 

-Xss<size>

设置JVM最大线程栈的空间大小

 

-Xprof

是否打印输出性能统计数据

 

-Xrunhprof

设置是否启动heapcpu等性能统计监控功能(详细见下表)

 

-Xdebug

设置是否启动远程调试功能

 

-Xfuture

 

 

-Xrs

设置是否屏蔽操作系统信号

 

-Xcheck:jni

设置对于本地调用是否执行额外检查

 


        1. JVM扩展参数集


Java HotspotGC相关参数介绍,下面以Solaris1.4.2版本为例,对于以–X打头的非标准参数,是不能保证在每个JVM的实现中都支持的,而且关于这些参数行为的改变都不会得到通知。


12.3.  非稳定(Stable)参数(-XX


而对于以–XX打头的非标准参数来说,它们中大多数都是和具体的操作系统支持有关的,而且有些甚至需要特殊的系统访问权限,而且这些参数也是遵循上述的改变不通知原则的。在使用中需要特别注意。


Sun的文档中,又分为三类:


行为参数Behavioral Options):用于改变jvm的一些基础行为;
性能调优Performance Tuning):用于jvm的性能调优;
调试参数Debugging Options):一般用于打开跟踪、打印、输出等jvm参数,用于显示jvm更加详细的信息;


12.3.1. 行为参数


 


参数及其默认值

描述

-XX:-DisableExplicitGC

禁止调用System.gc();但jvmgc仍然有效

-XX:+MaxFDLimit

最大化文件描述符的数量限制

-XX:+ScavengeBeforeFullGC

新生代GC优先于Full GC执行

-XX:+UseGCOverheadLimit

在抛出OOM之前限制jvm耗费在GC上的时间比例

-XX:-UseConcMarkSweepGC

对老生代采用并发标记交换算法进行GC

-XX:-UseParallelGC

启用并行GC

-XX:-UseParallelOldGC

Full GC启用并行,当-XX:-UseParallelGC启用时该项自动启用

-XX:-UseSerialGC

启用串行GC

-XX:+UseThreadPriorities

启用本地线程优先级


 


上面表格中黑体的三个参数代表着jvmGC执行的三种方式,即串行、并行、并发;


串行(SerialGCjvm的默认GC方式,一般适用于小型应用和单处理器,算法比较简单,GC效率也较高,但可能会给应用带来停顿;


并行(ParallelGC是指GC运行时,对应用程序运行没有影响,GCapp两者的线程在并行执行,这样可以最大限度不影响app的运行;


并发(ConcMarkSweepGC是指多个线程并发执行GC,一般适用于多处理器系统中,可以提高GC的效率,但算法复杂,系统消耗较大;


12.3.2. 性能调优


 


参数及其默认值

描述

-XX:LargePageSizeInBytes=4m

设置用于Java堆的大页面尺寸

-XX:MaxHeapFreeRatio=70

GCjava堆中空闲量占的最大比例

-XX:MaxNewSize=size

新生成对象能占用内存的最大值

-XX:MaxPermSize=64m

老生代对象能占用内存的最大值

-XX:MinHeapFreeRatio=40

GCjava堆中空闲量占的最小比例

-XX:NewRatio=2

新生代内存容量与老生代内存容量的比例

-XX:NewSize=2.125m

新生代对象生成时占用内存的默认值

-XX:ReservedCodeCacheSize=32m

保留代码占用的内存容量

-XX:ThreadStackSize=512

设置线程栈大小,若为0则使用系统默认值

-XX:+UseLargePages

使用大页面内存


 


12.3.3. 调试/打印信息参数


参数及其默认值

描述

-XX:-CITime

打印消耗在JIT编译的时间

-XX:ErrorFile=./hs_err_pid<pid>.log

保存错误日志或者数据到文件中

-XX:-ExtendedDTraceProbes

开启solaris特有的dtrace探针

-XX:HeapDumpPath=./java_pid<pid>.hprof

指定导出堆信息时的路径或文件名

-XX:HeapDumpOnOutOfMemoryError

当首次遭遇OOM时导出此时堆中相关信息

-XX:OnError="<cmd args>;<cmd args>"

出现致命ERROR之后运行自定义命令

-XX:OnOutOfMemoryError="<cmd args>;<cmd args>"

当首次遭遇OOM时执行自定义命令

-XX:-PrintClassHistogram

遇到Ctrl-Break后打印类实例的柱状信息,与jmap -histo功能相同

-XX:-PrintConcurrentLocks

遇到Ctrl-Break后打印并发锁的相关信息,与jstack -l功能相同

-XX:-PrintCommandLineFlags

打印在命令行中出现过的标记

-XX:-PrintCompilation

当一个方法被编译时打印相关信息

-XX:-PrintGC

每次GC时打印相关信息

-XX:-PrintGC Details

每次GC时打印详细信息

-XX:-PrintGCTimeStamps

打印每次GC的时间戳

-XX:-TraceClassLoading

跟踪类的加载信息

-XX:-TraceClassLoadingPreorder

跟踪被引用到的所有类的加载信息

-XX:-TraceClassResolution

跟踪常量池

-XX:-TraceClassUnloading

跟踪类的卸载信息

-XX:-TraceLoaderConstraints

跟踪类加载器约束的相关信息


 


 


12.3.4. 参数汇总(不含调试和垃圾收集参数)


 


参数

使用说明

备注

-XX:+AggressiveHeap

长时间大内存使用的优化,能检查计算资源(内存,处理器数量),至少需要256MB内存大量的CPU/内存,(在1.4.14CPU的机器上已经显示有提升)

 

-XX:+AggressiveOpts

加快编译

 

-XX:-AllowUserSignal

Handlers

允许用户在应用层设置信号处理回调函数

 

-XX:AltStackSize=16384

预备信号栈的大小

 

-XX:-CITime

设置Hotspot的一次即时编译所需要的最大时间

 

-XX:CompileThreshold

=10000

设置方法是否进行即时编译的调用次数的下限值,-server选项的缺省值为10000-client选项的缺省值为1500

即:当该方法的被调用测试多于该值时,则该方法就会被JIT即时编译器编译成机器代码在内存中执行

-XX:+DisableExplicitGC

屏蔽程序主动垃圾收集的函数system.gc()

 

-XX:FreqInlineSize=size

限制经常使用的动态编译的函数的虚拟机指令的最大数量,

 

-Xincgc

在垃圾收集中使用火车算法

 

-Xint

不启用即时编译(JIT)功能,仅仅解释执行

缺省为不选的

-XX:LargePageSizeInBytes

内存页的大小,不可设置过大,会影响Perm的大小。

 

 

 

 

-XX:MaxHeapFreeRatio

=<Maximum>

JVM中堆空间的最大空闲百分比,缺省为70%GC停止回收空间的上限值

即:一旦当前堆内存空闲空间百分比超过总空间70%时,GC暂停垃圾收集

-XX:MinHeapFreeRatio

=<Minimum>

JVM中堆空间的最小空闲百分比,缺省为40%GC开始回收空间的下限值

即:一旦当前内存堆中内存空闲小于40%时,GC则恢复垃圾收集

-XX:MaxInlineSize=size

限制动态编译的内联函数的虚拟机指令的最大数量

 

-XX:+MaxFDLimit

设置JVM进程打开最大文件句柄数(Solaris only

 

-XX:MaxNewSize=32m

在为新生代对象分配内存值,每块内存的最大值。

按代垃圾收集中使用

-XX:MaxTenuringThreshold=30

在存活区之间Copy的次数,超过该次数则移至Old区。

 

-Xmn

为新生代分配的内存大小。

 

-XX:NewRatio=2

新生代与老一代空间的比率,-XX:NewRatio = 2表示 Eden:old = 1:2SUN Parc –server中是21Intel中是121

 

-XX:NewSize=2228224

新一代的缺省申请空间的值

对于大型应用服务器系统这个值2K一般情况下需要调整大一些

-Xnoincgc

在垃圾收集中不使用火车算法

 

-XX:PreBlockSpin=10

 

 

-XX:-PrintTenuring

Distribution

打印使用年限

 

-XX:ReservedCodeCache

Size=32m

设置内存中保留代码缓冲区的大小

 

-XX:SoftRefLRUPolicyMSPerMB

相对于客户端模式的虚拟机(-client选项),当使用服务器模式的虚拟机时(-server选项),对于软引用(soft reference)的清理力度要稍微差一些。可以通过增大-XX:SoftRefLRUPolicyMSPerMB来降低收集频率。默认值是 1000,也就是说每秒一兆字节。Soft reference在虚拟机中比在客户集中存活的更长一些。其清除频率可以用命令行参数-XX:SoftRefLRUPolicyMSPerMB=<N>来控制,这可以指定每兆堆空闲空间的 soft reference保持存活(一旦它不强可达了)的毫秒数,这意味着每兆堆中的空闲空间中的 soft reference会(在最后一个强引用被回收之后)存活1秒钟。注意,这是一个近似的值,因为 soft reference只会在垃圾回收时才会被清除,而垃圾回收并不总在发生。

 

-XX:SurvivorRatio=64

存活区和eden区所占的比率:2:64

 

-XX:TargetSurvivorRatio

=50

该值是一个百分比,控制允许使用的生存区空间的比例,默认值是50。即占到50%,则执行Copy策略。该参数设置较大的话可提高对survivor空间的使用率。

 

-XX:ThreadStackSize

=512

每个线程栈大小(K),等于0时表示使用缺省值【Sparc: 512KSolaris Intel: 256KSparc 64bit: 1024其他的都为0

 

-XX:+UseBoundThreads

绑定用户级线程(Solaris only),这个选项强制所有的Java线程在创建时都作为操作系统绑定的线程

这个参数用来是否将JVM用户线程绑定到Solaris内核线程

-XX:+UseAltSigs

 

 

-XX:+UseV8InstrsOnly

 

 

-XX:-UseLWPSynchroniza

tion

使用操作系统提供的轻量级线程LWP同步来代替基于Java虚拟机的线程的同步

该参数的使用使得JVM将线程同步的控制交由Solaris内核处理,从而代替了JVM内部的线程同步机制

-XX:+UseThreadPriorities

设置是否使用本地线程优先级

 

-XX:-UseSpinning

 

 

-XX:+UseTLAB

是否使用线程本地对象分配策略,SUN Sparc –server时为true,其他为false

 

-XX:-UseISM

如果使用ISM选项可以获得如下的几个好处:1、使用大内存页来代替操作系统缺省的8K的页模式;

2、将一些内存页锁定在内存中,而不必换出到硬盘

 

如果系统使用ISM则系统文件/etc/system需要添加如下配置:

set shmsys:

shminfo_shmmax

=0xffffffff

set shmsys:

shminfo_shmseg=32

-XX:+UseFastAccessorMethods

原始类型的快速优化,get,set方法转成本地代码。

 

-XX:+UseBiasedLocking

锁机制的性能改善。

 


        1. JVM GC/Hotspot相关参数集


注:即时编译是Hotspot中的概念,按代收集,火车算法等是属于GC中的概念。


 


13.   项目调优经验1


  1. CMS垃圾收集器

    Date2010-12-24

    JVM参数:


-Xss2M -XX:PermSize=128M -XX:MaxPermSize=128M -Xms2G -Xmx2G -Xmn832M -XX:SurvivorRatio=6 -XX:TargetSurvivorRatio=80 -XX:ParallelGCThreads=8 -XX:+UseConcMarkSweepGC


观察分析:


  1. 新生代每次执行一次垃圾收集,旧生代增加一点:


旧生代:489,711 Kbà489,740Kb ;


新生代(638,976kb)651,878Kbà29,00kb


存活区(106,496kb):10,000kbà5,000kb


 


上述数据说明:对象在存活只存活一个周期,就被移到旧生代;相对应新生代每次收集存活区的最高利用率只有10%,根据上述参数可以把存活区Copy次数改为:(新生代分配值/存活区最大利用值)*目标存活区利用率=100M/10M*80%=8


 


  1. 垃圾收集数据

    新生代收集:ParNew29388次,用时:13m

    旧生代:ConurrentMarkSweep:0次,用时0s

     

    新生代平均每次收集暂停时间为:0.027s(平均每次用时:13m/29388=0.027s)。

    新生代收集29388次,旧生代0次:旧生代内存利用率远远低于新生代,可以适当放大旧生代,缩小新生代。

     

  2.  

     

    结论:扩大新生代内存,缩小旧生代内存,相应提升并发收集线程数量;增加-XX:MaxTenuringThreshold参数,设置值为8,要求对象在存活区之间Copy 10次在移动到旧生代。

     


  1. 并行垃圾收集自适应调整堆内存

     


**管控项目:64windows服务器


jvm 64: jdk1.5.0_16_b02


上线时间:2010.10,出现jvm不响应时间:2010.12.16


启动参数:


-server-Xss2M -XX:PermSize=64M -XX:MaxPermSize=64M


-XX:+UseParallelGC-XX:+UseAdaptiveSizePolicy -XX:+AggressiveHeap -XX:ParallelGCThreads=4


说明:采用并行垃圾回收器,堆内存通过JVM自适应调整(-XX:+UseAdaptiveSizePolicy-XX:+AggressiveHeap)。


 


问题:2010.12.16项目组发现运行在jvm上的引擎服务不响应,经分析是jvm在做垃圾全收集时,对jvm进行了一次自动重启。


 


方案:采用并发收集器,内存参数固定”,手动执行垃圾收集后,不出现jvm重启现象。


 


  1. 采用并发收集,内存固定


 


**管控项目:64windows服务器


jvm 64: jdk1.5.0_16_b02


 


启动参数:


-server-Xss2M -XX:PermSize=64M -XX:MaxPermSize=64M -Xms2G -Xmx2G -Xmn512M


-XX:SurvivorRatio=16-XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=10 -XX:ParallelGCThreads=4


 


分析:旧生代采用并发垃圾收集器(CMS)管理;新生代采用并行垃圾收集器(Parall);内存参数固定。


 


正常运行时间

1 hour 4 minutes

处理 CPU时间

6 minutes

JIT编译器

HotSpot 64-Bit Server Compiler

编译总时间

1 minute


 


垃圾收集器

Name = 'ParNew', Collections = 158, Total time spent = 9.392

垃圾收集器

Name = 'ConcurrentMarkSweep', Collections = 1, Total time spent = 2.076


 


 


 


14.   调优经验(摘抄)


14.1.  GC调优参数的使用


JVM中按代收集算法的基本原则是这样的,JVM堆空间被分成许多子堆,每个子堆用于存放不同代的对象,而当所有已经存在的堆中的各代对象都不能继续回收时,则新的子堆会被分配,用于存放新一代的对象,下面的这两个参数就是为按代收集设计的:


-XX:NewRatio=2              //新生代于老一代的空间比率


-XX:NewSize=2228224           //缺省时2K,对于应用服务器系统建议调整的大一些4K8K


-XX:MaxNewSize                    //新生代的最大空间


-XX:MaxPermSize=64m         //老一代的最大空间,缺省为64M,建议增加


-XX:SurvivorRatio                  //GC时代子堆中的年老对象的比率


-Xxincgc                           //是否在垃圾收集时启用火车算法


-XX:+UseConcMarkSweepGC//是否启用并发收集算法


-Xverifyheap              //仅仅用于Debug版本,用于对GC数据分析


-XX:TargetSurvivorRatio=50   //GC收集后期望得到的被老一代占用的空间,建议不调整


 


因为垃圾收集只是在各代的子堆满了的时候发生,总的堆的空间状况也会对垃圾收集产生重要的影响,JVM向操作系统申请更多的堆内存空间的前提是,堆中所有的年老的代的子堆都已经满了。


-Xms                          //设置最小初始堆空间的大小


-Xmx                          //设置最大堆空间的大小


-XX:MinFreeHeapRatio=40     //GCJVM堆空间向操作系统缩小的比率。


-XX:MaxHeapFreeRatio70  //GCJVM堆空间向操作系统扩张的比率。


-XX:+AggressiveHeap            //用于JVM运行于大内存模式下,JVM的堆空间至少在1G以上,与-Xms-Xmx不同时使用,慎用!会导致JVM极度消/耗内存


U-NICA这样的后台大型应用服务器系统来说,我们就采用了通过调整初始堆内存空间、堆增长量、增加新生代空间配置、使用并发收集算法、火车算法等方法来使的系统的垃圾收集能力得以优化。而且在具体实际测试过程中也发现这些调整通常是有效而且成本低廉的。


14.2.  JIT调优参数的使用


从解释执行到即时编译,再到热点编译JVM走在一条不断优化的道路上,今天我们通过一些简单的参数设置就可以获得以前梦想的性能,对于Java用户来说,这的确是一条捷径:


-server                       //很多Hotspot的能力都是通过这个选项打开的,对于大型服


//务器尤为重要,这里可以启动热点编译功能


-Xmaxjitcodesize32m       //设置即时编译代码的最大尺寸


-Xint                           //纯解释执行,一般情况下不用它


-Xtime                        //不太清楚如何使用,总之是指定JIT的时间


-XX:+DisableExplicitGC          //是否屏蔽应用层的垃圾收集请求


-XX:-UseISM                    //使用大内存页模式则会减少GC的时间


-XX:-UseMPSS                //在使用ISM选项的同时不使用该选项,否则ISM无效


14.3.  Java线程调优参数的使用


-XX:-UseBoundThreads          //如果你的系统是Solaris8以上,尽情使用这个参数吧,操作系统内核的线程调度总是要必应用层的调度快一些


-XX:-UseLWPSynchronization//这个参数也是同样的,让操作系统来做线程同步这些工作


-XX:+UseThreadPriorities       //是否采用操作系统内部定义的线程优先级


-XX:CompileThreshold=10000//如果你想让你的系统更早变快一些,而且你的内存足够多的话,可以将这个参数值调小


-XX:PreBlockSpin=10             //仅仅用于Linux版本


-XX:ThreadStackSize=512     //设置线程栈的大小,如果你的应用中有比较大的循环或递


归时使用。


-XX:+UseTLAB                //是否在线程的栈空间内分配对象,如果你的内存较大的话,并且配置了比较大的线程栈空间,则使用这个参数会使得临时&本地对象的申请和释放比较快。



版权声明:本文为博主原创文章,未经博主允许不得转载。 http
本系列包括:


注:本文部分内容收集整理了网上的资料。

1.      内存结构


1.1.     分代结构图


注意:


JVM中,非堆内存,根据模式不同分为不同的几个部分。


-Server下:非堆包括:持久代和代码缓存(Code cache


-client下:非堆包括:持久代、代码缓存(Code cache)、Perm rwperm ro


2.      性能指标


对于垃圾收集的性能,主要有两个指标。


吞吐量throughput)指用来进行垃圾收集之外工作所用的时间占总时间的百分比,一般要通过长时间的观察和测量。吞吐量包括了分配内存所花费的时间在内(一般来说无需对分配进行调优)。


Throughput is the percentage of total time not spent in garbage collection,considered over long periods of time. Throughput includes time spent inallocation (but tuning for speed of allocation is generally not needed.) fromTuning GC with jdk 1.5


暂停Pause)指由于进行垃圾收集而导致应用无法响应的时间。


Pauses are the times when an application appears unresponsive becausegarbage collection is occurring.


Footprint is the working set of a process, measured in pages and cachelines. On systems with limited physical memory or many processes, footprint maydictate scalability.      Promptnessis the time between when an object becomes dead and when the memory becomesavailable, an important consideration for distributed systems, including remotemethod invocation (RMI). fromTuning GC with jdk 1.5


用户对于垃圾收集有不同的需求,例如,对于Web服务器应用来说,吞吐量是要着重考虑的,而暂停时间可能由于网络的反应时间而不那么明显;而对于一个交互式图形界面的应用来说,即使是短暂的暂停都会带来非常不好的用户体验。


In general, aparticular generation sizing chooses a trade-off between these considerations.For example, a very largeyoung generation may maximize throughput,but does so at the expense of footprint, promptness, and pause times.younggeneration pauses can be minimized by using a smallyoung generationat the expense of throughput. To a first approximation, the sizing of onegeneration does not affect the collection frequency and pause times for anothergeneration .fromTuning GC with jdk 1.5


通常来说,如何设置代的大小是在这些考虑因素之间作出的一个权衡。例如,将新生代设置得很大会得到很好的吞吐性能,但是会增加暂停时间;反之,较小的新生代设置会减小暂停时间,但是降低了吞吐量。


一个代的大小不应该影响在其他代上进行垃圾收集的频率和暂停时间。


3.      引用类型


对象引用类型分为强引用、软引用、弱引用和虚引用


3.1.     强引用


就是我们一般声明对象是时虚拟机生成的引用,强引用环境下,垃圾回收时需要严格判断当前对象是否被强引用,如果被强引用,则不会被垃圾回收。


3.2.     软引用


软引用一般被做为缓存来使用。与强引用的区别是,软引用在垃圾回收时,虚拟机会根据当前系统的剩余内存来决定是否对软引用进行回收。如果剩余内存比较紧张,则虚拟机会回收软引用所引用的空间;如果剩余内存相对富裕,则不会进行回收。换句话说,虚拟机在发生OutOfMemory时,肯定是没有软引用存在的。


3.3.     弱引用


弱引用与软引用类似,都是作为缓存来使用。但与软引用不同,弱引用在进行垃圾回收时,是一定会被回收掉的,因此其生命周期只存在于一个垃圾回收周期内。


3.4.     虚引用


 


4.      内存分配回收次序


新生代是类的诞生、成长、消亡的区域,一个类在这里产生,应用,最后被垃圾回收器收集,结束生命。


新生区又分为两部分:伊甸区(Eden space)和幸存者区(Survivorpace),所有的类都是在endennew出来的。幸存区有两个: 0区(Survivor 0 space)和1区(Survivor 1 space)。


Eden的空间用完时,程序又需要创建对象,JVM的垃圾回收器将对enden区进行垃圾回收,将enden区中的不再被其他对象所引用的对象进行销毁(次收集)。


然后将伊甸园中的剩余对象移动到幸存0区。若幸存0区也满了,再对该区进行垃圾回收(次收集),然后移动到1区。那如果1区也满了呢?(次收集)


再移动到养老区。


 



新生代采用复制算法,old区采用标记清除算法。


无论是复制算法还是标记清除算法,在垃圾收集期间都要暂停客户的应用程序(cms垃圾收集器除外,它在初始标记和重新标记时暂停,并发标记和并发清除时,客户线程不暂停)。


无论是复制算法还是标记清除算法,在最开始,倒要标记活动的对象,即对象的可达性分析,在这里,必须要求对象是一致的(即可达性分析期间,内存状态是冻结的);在cms收集器是,在初始标记(对root对象标记时,要求冻结)冻结,但此时会产生浮动垃圾,即在并发标记时,由分配了新的对象(因为没有冻结)。


综述,基本过程是,复制算法:标记(暂停)-->复制(暂停,因对象地址发生变化);标记清除整理算法:标记(暂停)-->清除整理(消除碎片,暂停,因对象地址发生变化)。


root对象标记很快,对内存扫描分析,可达性分析过程很慢。清除很慢(内存回收),内存整理、复制(慢)。


内存分配需要时间。


5.      对象标记算法(Object Marking Algorithms


5.1.     引用计数法(ReferenceCounting


堆中每一个对象都有一个引用计数。当新创建一个对象,或者有变量被赋值为这个对象的引用,则这个对象的引用计数加1;当一个对象的引用超过生存期或者被设置一个新的值时,这个对象的引用计数减1。当对象的引用计数变为0时,就可以被当作垃圾收集。


这种方法的好处是垃圾收集较快,适用于实时环境。缺点是这种方法无法监测出循环引用。例如对象A引用对象B,对象B也引用对象A,则这两个对象可能无法被垃圾收集器收集。因此这种方法是垃圾收集的早期策略,现在很少使用。


5.2.     根搜索算法(Garbage Collection Roots Tracing


5.2.1.    基本思想


这种方法把每个对象看作图中一个节点,对象之间的引用关系为图中各节点的邻接关系。垃圾收集器从一个或数个根结点遍历对象图,如果有些对象节点永远无法到达,则这个对象可以被当作垃圾回收。


容易发现,这种方法可以检测出循环引用,避免了引用计数法的缺点,较为常用。步骤如下:


  1. 选定一些对象,作为 GC Roots,组成基对象集;

  2. 由基对象集内的对象出发,搜索所有可达的对象;

  3. 其余的不可达的对象,就是可以被回收的对象。

    这里的可达不可达与图论中的定义一样,所有的对象被看做点,引用被看做有向连接,整个引用关系就是一个有向图。在引用计数法中提到的循环引用,其实就是有向图中有环的情况,即构成有向有环图。引用计数法不适用于有向有环图,而根搜索算法适用于所有有向图,包括有环的和无环的。


5.2.2.    GCRoots


如果你的逻辑思维够清晰,你会说一定与选取基对象集的方法有关。是的,没错。选取 GC Roots 组成基对象集,其实就是选取如下这些对象:


《深入理解 Java 虚拟机:JVM高级特性与最佳实践》一书中提到的 GC Roots 为:


1.     方法区(Method Area,即 Non-Heap)中的类的 static 成员引用的对象,和 final成员引用的对象;


2.     Java方法栈(Java Method Stack)的局部变量表(Local Variable Table)中引用的对象;


3.     原生方法栈(Native Method Stack)中 JNI中引用的对象。


但显然不够全面,[参考2]中提到的要更全面:(March 6th,2012 update


1.     由系统类加载器加载的类相应的对象:这些类永远不会被卸载,且这些类创建的对象都是 static 的。注意用户使用的类加载器加载的类创建的对象,不属于 GC Roots,除非是 java.lang.Class 的相应实例有可能会称为其他类的 GC Roots


2.     正在运行的线程。


3.     Java方法栈(Java Method Stack)的局部变量表(Local Variable Table)中引用的对象。


4.     原生方法栈(Native Method Stack)的局部变量表(Local Variable Table)中引用的对象。


5.     JNI中引用的对象。


6.     同步监控器使用的对象。


7.      JVM  GC控制的对象:这些对象是用于 JVM内部的,是实现相关的。一般情况下,可能包括系统类加载器(注意与“1”不一样,“1”中是 objects created by the classes loaded bysystem class loaders,这里是 theobjects, corresponding instances of system class loaders)、JVM内部的一些重要的异常类的对象、异常句柄的预分配对象和在类加载过程中自定义的类加载器。不幸的是,JVM并不提供这些对象的任何额外的详细信息。因此这些实现相关的内容,需要依靠分析来判定。


所以这个算法实施起来有两部分,第一部分就是到 JVM 的几个内存区域中找对象,第二部分就是运用图论算法


 


 


 


 


6.      垃圾回收算法


6.1.     标记-清除(Mark-Sweep



此算法执行分两阶段。第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除。此算法需要暂停整个应用,同时,会产生内存碎片。


6.2.     标记-整理(Mark-Compact


此算法结合了标记-清除复制两个算法的优点。也是分两阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,把清除未标记对象并且把存活对象压缩到堆的其中一块,按顺序排放。此算法避免了标记-清除的碎片问题,同时也避免了复制算法的空间问题。


6.3.     复制(Copying


此算法把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中。次算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理,不会出现碎片问题。当然,此算法的缺点也是很明显的,就是需要两倍内存空间


6.4.     增量收集算法


增量收集器把堆栈分为多个域,每次仅从一个域收集垃圾。这会造成较小的应用程序中断。


6.5.     分代收集算法


这种收集器把堆栈分为两个或多个域,用以存放不同寿命的对象。虚拟机生成的新对象一般放在其中的某个域中。过一段时间,继续存在的对象将获得使用期并转入更长寿命的域中。分代收集器对不同的域使用不同的算法以优化性能。这样可以减少复制对象的时间。


6.6.     并发收集算法


并发收集器与应用程序同时运行。这些收集器在某点上(比如压缩时)一般都不得不停止其他操作以完成特定的任务,但是因为其他应用程序可进行其他的后台操作,所以中断其他处理的实际时间大大降低。


6.7.     并行收集器


并行收集器使用某种传统的算法并使用多线程并行的执行它们的工作。在多CPU机器上使用多线程技术可以显著的提高java应用程序的可扩展性。


6.8.     自适应收集器


根据程序运行状况以及堆的使用状况,自动选一种合适的垃圾回收算法。这样可以不局限与一种垃圾回收算法。


6.9.     火车增量算法


垃圾收集算法一个很大的缺点就是难以控制垃圾回收所占用的CPU时间,以及何时需要进行垃圾回收。火车算法是分代收集器所用的算法,目的是在成熟对象空间中提供限定时间的渐进收集。目前应用于SUN公司的Hotspot虚拟机上。


在火车算法中,内存被分为块,多个块组成一个集合。为了形象化,一节车厢代表一个块,一列火车代表一个集合,见图一


图一


注意每个车厢大小相等,但每个火车包含的车厢数不一定相等。垃圾收集以车厢为单位,收集顺序依次为1.11.21.31.42.12.22.33.13.23.3。这个顺序也是块被创建的先后顺序。


垃圾收集器先从块1.1开始扫描直到1.4,如果火车1四个块中的所有对象没有被火车2和火车3的对象引用,而只有火车1内部的对象相互引用,则整个火车1都是垃圾,可以被回收。


如图二,车厢1.1中有对象A和对象B1.3中有对象C1.4中有对象D,车厢2.2中有对象E,车厢3.3中有对象F。在火车1中,对象C引用对象A,对象B引用对象D,可见,火车2和火车3没有引用火车1的对象,则整个火车1都是垃圾。


图二


如果火车1中有对象被其它火车引用,见图三,扫描车厢1.1时发现对象A被火车2中的E引用,则将对象A从车厢1.1转移到车厢2.2,然后扫描A引用的对象D,把D也转移到车厢2.2,然后扫描D,看D是否引用其它对象,如果引用了其它对象则也要转移,依次类推。扫描完火车1的所有对象后,剩下的没有转移的对象都是垃圾,可以把整个火车1都作为垃圾回收。注意如果在转移时,如果车厢2.2空间满了,则要在火车2末尾开辟新的车厢2.4,将新转移的对象都放到2.4,即火车的尾部)


图三


补充说明:垃圾回收器一次只扫描一个车厢。图三中的对象BC并不是立即被回收,而是先会被转移到火车1的尾部车厢。即扫描完1.1后,B被转移到火车1尾部,扫描完1.3后,C被转移到车尾。等垃圾收集器扫描到火车1尾部时,如果仍然没有外部对象引用它们,则BC会被收集。


火车算法最大的好处是它可以保证大的循环结构可以被完全收集,因为成为垃圾的循环结构中的对象,无论多大,都会被移入同一列火车,最终一起被收集。还有一个好处是这种算法在大多数情况下可以保证一次垃圾收集所耗时间在一定限度之内,因为一次垃圾回收只收集一个车厢,而车厢的大小是有限度的。


 


 


7.      触发垃圾收集的条件


由于对象进行了分代处理,因此垃圾回收区域、时间也不一样。GC有两种类型:Scavenge GCFull GC


7.1.     次收集ScavengeGC


一般情况下,当Enden区对象已满,或当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区进行,不会影响到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden去能尽快空闲出来。


Survivor区,当达到设置的预定值(默认50%),则进行一次垃圾次收集。


7.2.     全收集FullGC


对整个堆进行整理,包括YoungTenuredPermFull GC因为需要对整个对进行回收,所以比Scavenge GC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分工作就是对于FullGC的调节。有如下原因可能导致Full GC


·年老代(Tenured)被写满


·持久代(Perm)被写满


· System.gc()被显示调用


·上一次GC之后Heap的各域分配策略动态变化


8.      垃圾收集器类型


8.1.     串行收集器


用单线程处理所有垃圾回收工作,因为无需多线程交互,所以效率比较高。但是,也无法使用多处理器的优势,所以此收集器适合单处理器机器。当然,此收集器也可以用在小数据量(100M左右)情况下的多处理器机器上。可以使用-XX:+UseSerialGC打开。


8.2.     并行收集器


注:图有问题,是并发垃圾收集器的图。


并行垃圾回收,因此可以减少垃圾回收时间。一般在多线程多处理器机器上使用。


8.3.     并发垃圾收集


并发收集器主要减少年老代的暂停时间,他在应用不停止的情况下使用独立的垃圾回收线程,跟踪可达对象。在每个年老代垃圾回收周期中,在收集初期并发收集器会对整个应用进行简短的暂停,在收集中还会再暂停一次。第二次暂停会比第一次稍长,在此过程中多个线程同时进行垃圾回收工作。


浮动垃圾:由于在应用运行的同时进行垃圾回收,所以有些垃圾可能在垃圾回收进行完成时产生,这样就造成了“Floating Garbage”,这些垃圾需要在下次垃圾回收周期时才能回收掉。所以,并发收集器一般需要20%的预留空间用于这些浮动垃圾。


Concurrent Mode Failure:并发收集器在应用运行时进行收集,所以需要保证堆在垃圾回收的这段时间有足够的空间供程序使用,否则,垃圾回收还未完成,堆空间先满了。这种情况下将会发生并发模式失败,此时整个应用将会暂停,进行垃圾回收。


启动并发收集器:因为并发收集在应用运行时进行收集,所以必须保证收集完成之前有足够的内存空间供程序使用,否则会出现“Concurrent Mode Failure”。通过设置-XX:CMSInitiatingOccupancyFraction=<N>指定old区达到百分之多少时开始执行并发收集。


9.      垃圾收集器介绍


9.1.     串行垃圾收集


Serial Collector是指任何时刻都只有一个线程进行垃圾收集,这种策略有一个名字“stop the whole world",它需要停止整个应用的执行。这种类型的收集器适合于单器CPU的机器。


Serial Copying Collector


此种GC-XX:UseSerialGC选项配置,它只用于新生代对象的收集。1.5.0以后.


-XX:MaxTenuringThreshold来设置对象复制的次数。当eden空间不够的时候,GC会将eden的活跃对象和一个名叫From survivor空间中尚不够资格放入Old代的对象复制到另外一个名字叫To Survivor的空间。而此参数就是用来说明到底From survivor中的哪些对象不够资格,假如这个参数设置为31,那么也就是说只有对象复制31次以后才算是有资格的对象。


这里需要注意几个个问题:


  • From SurvivorTo survivor的角色是不断的变化的,同一时间只有一块空间处于使用状态,这个空间就叫做From Survivor区,当复制一次后角色就发生了变化。

  • 如果复制的过程中发现To survivor空间已经满了,那么就直接复制到old generation.

  • 比较大的对象也会直接复制到Old generation,在开发中,我们应该尽量避免这种情况的发生。

     

     

    并行垃圾收集(Parallel Collector

    Parallel Copying Collector

    -XX:+UseParNewGC用来设置年轻代为并发收集【多CPU】,如果你的服务器有多个CPU,你可以开启此参数;开启此参数,多个CPU可并发进行垃圾回收,可提高垃圾回收的速度。此参数和-XX:ParallelGCThreads搭配使用。适用于1.4以后版本。


Parallel Mark-Compact Collector


此种GC-XX:+UseParallelOldGC参数配置,此GC主要用于老生代对象的收集。适用于1.6.0以后版本。


Parallel scavenging Collector


-XX:+UseParallelGC它是对新生代对象的垃圾收集器。年轻代使用并行收集,而年老代仍旧使用串行收集。可提高系统的吞吐量。此参数和-XX:ParallelGCThreads搭配使用。适用于1.4以后版本。它比较适合于对吞吐量高于暂停时间的场合。


 


采用多线程并行(Parallel)进行垃圾收集。


9.3.     并发垃圾收集器(Concurrent Collector)


此种GC可以用参数-XX:+UseConcMarkSweepGC配置,此GC主要用于老生代和Perm代的收集。


Concurrent Collector通过并发的方式进行垃圾收集,这样就减少了垃圾收集器收集一次的时间,这种GC在实时性要求高于吞吐量的时候比较有用。


在应用运行的同时,并发(Concurrent)进行垃圾收集。


  1. 配置垃圾回收线程数量

             -XX:ParallelGCThreads年轻代并行垃圾收集的前提下(对并发也有效果)的线程数,增加并行度,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。
    例如:-XX:ParallelGCThreads=2

  2. 配置垃圾收集器需要注意的地方

    垃圾收集器不可重复配置,例如已经配置了新生代并行收集(-XX:+UseParNewGC),此时不能配置任何对新生代的垃圾收集。可以配置对旧生代和持久对象的垃圾收集。

    例如:

    -XX:+UseParNewGC-XX:+UseConcMarkSweepGC -XX:ParallelGCThreads=2

    可组合适用。

    增量

    配合并发垃圾收集,适用于单cpu的架构,sun不建议使用。

     

    自适应

    自适应垃圾收集,运行在并行垃圾收集上。在并行收集时对于收集时间、分配比例、收集之堆的空闲空间等数据进行统计分析,然后以此为依据调整新生代和旧生代的大小以达到最佳效果。

     

     

    小结

             J2SE平台1.4.2版本开始,加入了另外3种垃圾收集器,都是着重提高吞吐能力,降低垃圾收集时的暂停时间。

             1. 吞吐收集器(throughput collector):命令行参数:-XX:+UseParallelGC。在新生代使用并行收集策略,在旧生代和默认收集器相同。

             吞吐收集器和默认的收集器类似,都是分代收集器。不同之处就在于吞吐收集器用多线程进行次要收集,

            J2SE平台1.4.1版本开始,吞吐收集器就具有了一个特征,就是大小自适应(参数-XX:

    +UseAdaptiveSizePolicy),这个选项默认是打开的。该特征对于收集时间、分配比例、收集之   堆的空闲空间等数据进行统计分析,然后以此为依据调整新生代和旧生代的大小以达到最佳效果。可以使用-verbose:gc来查看堆的大小。

             -XX:+AggressiveHeap选项会检测主机的资源(内存大小、处理器数量),然后调整相关的参数,使得长时间运行的、内存申请密集的任务能够以最佳状态运行。该选项最初是为拥有大量内存和很多处理器的主机而设计的,但是从J2SE1.4.1以及其后继版本来看,即使是对于那些只有4CPU       主机,该选项都是很有帮助的。因此,吞吐收集器(-XX:+UseParallelGC)、大小自适应 -XX:+UseAdaptiveSizePolicy)以及本选项(-XX:+AggressiveHeap)经常结合在一起使用。要使    用本选项,主机上至少要有256M的物理内存,堆内存的最初大小是基于物理内存计算出来的,然后 会根据需要尽可能的利用物理内存。

           2. 并发收集器(concurrent low pause collector):命令行参数:-XX:+UseConcMarkSweepGC。在旧生代使用并发收集策略,大部分收集工作都是和应用并发进行的,在进行收集的时候,应用的暂停时间很短。如果综合使用-XX:+UseParNewGC-XX:+UseConcMarkSweepGC,那么在新生代上使用并行的收集策略。

             3. 增量收集器(incremental low pause collector):命令行参数:-Xincgc。使用增量收集器要谨慎,他只是在每次进行次要收集的时候对旧生代进行一部分的收集,这样就把主要收集所带来的较长时间的停顿分散到多次的次要收集。但是,考虑到总共的吞吐,可能比旧生代上默认的收集还要慢。

    注意,-XX:+UseParallelGCXX:+UseConcMarkSweepGC不能同时使用。对于J2SE1.4.2版本会检查垃圾收集相关参数组合的合法性,但是对于之前的版本没有这个检查,可能会导致不可预知的错误。

    垃圾回收器相关参数

    串行垃圾收集器参数

     


参数

说明

-XX:+UseSerialGC

设置串行收集器

 

 


 


10.2.  并行垃圾收集器参数


参数

说明

-XX:+UseParallelGC

选择垃圾收集器为并行收集器,此配置仅对年轻代有效,即上述配置下,年轻代使用并行收集,而老年代仍旧使用串行收集。采用了多线程并行管理和回收垃圾对象,提高了回收效率,提高了服务器的吞吐量,适合于多处理器的服务器。

-XX:ParallelGCThreads

配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。

-XX:+UseParallelOldGC

采用对于老年代并发收集的策略,可以提高收集效率。JDK6.0支持对老年代并行收集。

-XX:MaxGCPauseMillis

设置每次年轻代并行收集最大暂停时间,如果无法满足此时间,JVM会自动调整年轻代大小以满足此值。

-XX:+UseAdaptiveSizePolicy

设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低响应时间或者收集频率等,此值建议使用并行收集器时,一直打开。

-XX:+UseParNewGC

指定在 New Generation使用 parallel collector, UseParallelGC gc的升级版本 ,有更好的性能或者优点 ,可以和 CMS gc一起使用


 


 


10.3.  并发垃圾收集器参数


参数

说明

-XX:+UseConcMarkSweepGC

指定在老年代使用 concurrent cmark sweep gcgc thread app thread并行 ( init-mark remark pause app thread)app pause时间较短 ,适合交互性强的系统 , web server。它可以并发执行收集操作,降低应用停止时间,同时它也是并行处理模式,可以有效地利用多处理器的系统的多进程处理。

-XX:+UseCMSCompactAtFullCollection

打开对老年代的压缩。可能会影响性能,但是可以消除碎片,FULL GC的时候,压缩内存, CMS是不会移动内存的,因此,这个非常容易产生碎片,导致内存不够用,因此,内存的压缩这个时候就会被启用。增加这个参数是个好习惯。

-XX:+CMSIncrementalMode

设置为增量模式。适用于单CPU情况

-XX:CMSFullGCsBeforeCompaction

由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生碎片,使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。

-XX:+CMSClassUnloadingEnabled

使CMS收集持久代的类,而不是fullgc

-XX:+CMSPermGenSweepingEnabled

使CMS收集持久代的类,而不是fullgc

-XX:-CMSParallelRemarkEnabled

在使用 UseParNewGC的情况下 ,尽量减少 mark的时间。

-XX:CMSInitiatingOccupancyFraction

说明老年代到百分之多少满的时候开始执行对老年代的并发垃圾回收(CMS),这个参数设置有很大技巧,基本上满足公式:

(Xmx-Xmn)*(100-CMSInitiatingOccupancyFraction)/100>=Xmn

时就不会出现promotion failed。在我的应用中Xmx6000Xmn500,那么Xmx-Xmn5500兆,也就是老年代有5500兆,CMSInitiatingOccupancyFraction=90说明老年代到90%满的时候开始执行对老年代的并发垃圾回收(CMS),这时还剩10%的空间是5500*10%=550兆,所以即使Xmn(也就是年轻代共500兆)里所有对象都搬到老年代里,550兆的空间也足够了,所以只要满足上面的公式,就不会出现垃圾回收时的promotion failed

如果按照Xmx=2048,Xmn=768的比例计算,则CMSInitiatingOccupancyFraction的值不能超过40,否则就容易出现垃圾回收时的promotion failed

-XX:+UseCMSInitiatingOccupancyOnly

指示只有在老年代在使用了初始化的比例后 concurrent collector 启动收集


 


11.   JVM内存溢出类型


对于JVM的内存写过的文章已经有点多了,而且有点烂了,不过说那么多大多数在解决OOM的情况,于此,本文就只阐述这个内容,携带一些分析和理解和部分扩展内容,也就是JVM宕机中的一些问题,OK,下面说下OOM的常见情况:


第一类内存溢出,就是堆栈溢出:关键字:java.lang.OutOfMemoryError: ......javaheap space.....


也就是当你看到heap相关的时候就肯定是堆栈溢出了,此时如果代码没有问题的情况下,适当调整-Xmx-Xms是可以避免的,不过一定是代码没有问题的前提,为什么会溢出呢,要么代码有问题,要么访问量太多并且每个访问的时间太长或者数据太多,导致数据释放不掉,因为垃圾回收器是要找到那些是垃圾才能回收,这里它不会认为这些东西是垃圾,自然不会去回收了;主意这个溢出之前,可能系统会提前先报错关键字为:


java.lang.OutOfMemoryError:GC over headlimit exceeded


这种情况是当系统处于高频的GC状态,而且回收的效果依然不佳的情况,就会开始报这个错误,这种情况一般是产生了很多不可以被释放的对象,有可能是引用使用不当导致,或申请大对象导致,但是java heap space的内存溢出有可能提前不会报这个错误,也就是可能内存就直接不够导致,而不是高频GC.


第二类内存溢出,PermGen的溢出,或者PermGen满了的提示,你会看到这样的关键字:关键信息为:java.lang.OutOfMemoryError: PermGenspace


原因:系统的代码非常多或引用的第三方包非常多、或代码中使用了大量的常量、或通过intern注入常量、或者通过动态代码加载等方法,导致常量池的膨胀,虽然JDK 1.5以后可以通过设置对永久带进行回收,但是我们希望的是这个地方是不做GC的,它够用就行,所以一般情况下今年少做类似的操作,所以在面对这种情况常用的手段是:增加-XX:PermSize-XX:MaxPermSize的大小。


第三类内存溢出:在使用ByteBuffer中的allocateDirect()的时候会用到,很多javaNIO的框架中被封装为其他的方法溢出关键字:java.lang.OutOfMemoryError: Directbuffer memory


如果你在直接或间接使用了ByteBuffer中的allocateDirect方法的时候,而不做clear的时候就会出现类似的问题,常规的引用程序IO输出存在一个内核态与用户态的转换过程,也就是对应直接内存与非直接内存,如果常规的应用程序你要将一个文件的内容输出到客户端需要通过OS的直接内存转换拷贝到程序的非直接内存(也就是heap中),然后再输出到直接内存由操作系统发送出去,而直接内存就是由OS和应用程序共同管理的,而非直接内存可以直接由应用程序自己控制的内存,jvm垃圾回收不会回收掉直接内存这部分的内存,所以要注意了哦。


如果经常有类似的操作,可以考虑设置参数:-XX:MaxDirectMemorySize


第四类内存溢出错误:溢出关键字:java.lang.StackOverflowError


这个参数直接说明一个内容,就是-Xss太小了,我们申请很多局部调用的栈针等内容是存放在用户当前所持有的线程中的,线程在jdk 1.4以前默认是256K1.5以后是1M,如果报这个错,只能说明-Xss设置得太小,当然有些厂商的JVM不是这个参数,本文仅仅针对Hotspot VM而已;不过在有必要的情况下可以对系统做一些优化,使得-Xss的值是可用的。


第五类内存溢出错误:溢出关键字:java.lang.OutOfMemoryError: unable tocreate new native thread


上面第四种溢出错误,已经说明了线程的内存空间,其实线程基本只占用heap以外的内存区域,也就是这个错误说明除了heap以外的区域,无法为线程分配一块内存区域了,这个要么是内存本身就不够,要么heap的空间设置得太大了,导致了剩余的内存已经不多了,而由于线程本身要占用内存,所以就不够用了,说明了原因,如何去修改,不用我多说,你懂的。


第六类内存溢出:溢出关键字java.lang.OutOfMemoryError: request {}byte for {}out of swap


这类错误一般是由于地址空间不够而导致。


六大类常见溢出已经说明JVM99%的溢出情况,要逃出这些溢出情况非常困难,除非一些很怪异的故障问题会发生,比如由于物理内存的硬件问题,导致了code cache的错误(在由byte code转换为native code的过程中出现,但是概率极低),这种情况内存会被直接crash掉,类似还有swap的频繁交互在部分系统中会导致系统直接被crash掉,OS地址空间不够的话,系统根本无法启动,呵呵;JNI的滥用也会导致一些本地内存无法释放的问题,所以尽量避开JNIsocket连接数据打开过多的socket也会报类似:IOException: Too many open files等错误信息。


JNI就不用多说了,尽量少用,除非你的代码太牛B了,我无话可说,呵呵,这种内存如果没有在被调用的语言内部将内存释放掉(如C语言),那么在进程结束前这些内存永远释放不掉,解决办法只有一个就是将进程kill掉。


另外GC本身是需要内存空间的,因为在运算和中间数据转换过程中都需要有内存,所以你要保证GC的时候有足够的内存哦,如果没有的话GC的过程将会非常的缓慢。


 


12.   JAVA虚拟机参数分类说明


12.1.  标准参数-


所谓的Java虚拟机标准参数指的就是所有的虚拟机实现都应该支持的参数,这部分参数基本上都是对虚拟机基本能力的调整,包括对运行模式、垃圾收集信息显示、显示版本信息、断言开关等,下面是以Solaris1.4.2版本为例:


参数

使用说明

备注

-d32

-d64

指明该Java VM是运行与32位环境还是64位环境,默认是运行在32位环境下的,如果是配置了64位模式则需要操作系统也必须是64位的,当然CPU更需要是64位的。另外如果我们选择了-server参数,则就暗含了64位模式。

因为64CPU兼容32位操作系统,而64位操作系统又是兼容32位执行程序

-client

-server

设置该JVM运行与Client或者Server Hotspot模式,这两种模式从本质上来说是在JVM中运行不同的JIT(运行时编译模块)代码,并且两者在JVM内部的接口是一致的。客户端模式优化的是系统启动时间更快,而服务端模式的优化则更关注与系统的整体性能。一般来说Client选项用于GUI的应用,Server选项多用于后台服务器应用。

另外两者在编译策略、垃圾收集策略、堆使用上也有所不同

-hotspot

Hotspot类型的JVM中缺省使用,缺省为Client Hotspot模式。

 

-cp

-classpath

指明JVM启动时要加载的类文件路径,Java虚拟机进程在启动时就会按照该参数后面指明的路径查找*.zip*.jar*.class文件,然后将这些包中的类文件加载到内存中。

JVM加载类文件的顺序是

-D<name>=<value>

设置系统属性的值,该参数是的设计是为了满足Java应用程序员与JVM进行参数传递的手段之一,另一种是通过应用级参数(argument)来实现。

Java程序员可以在程序内调用system.getProperty来获取用户通过-D参数传进来的系统属性信息。而命令行参数就是是JVM传递给main函数的调用参数

 

-verbose:class

 

-verbose:gc

 

-verbose:jni

打印详细信息,目前支持打印类加载信息:class、垃圾收集信息:gc、以及本地方法调用信息:jni,如果选择了此选项,则JVM会在命令行打印出上述信息;

对于测试中的系统可以通过打开:gc开关,查看JVM每次垃圾收集的详细信息来判断系统内存消耗情况,如果系统垃圾收集的很频繁,而且每次都回收了大量的内存,则说明系统内存消耗很大,对象的创建和湮灭很频繁,而如果堆内存一直保持着增长的话,说明可能存在内存“泄漏”。

-version

-showversion

-version选项是显示版本信息后JVM退出

-showversion选项是显示版本信息后JVM继续运行

 

-esa

-enableassertions

打开系统中每个类的断言开关

该选项用于程序开发、调试过程

-da

-disableassertions

关闭系统中每个类的断言开关

该选项用于程序开发、调试过程

 

 

 


        1. JVM标准参数集


12.2.  扩展参数-X


所谓的Java虚拟机非标准参数指的就是一些特有的虚拟机实现所支持,下面以Solaris1.4.2版本为例介绍一些扩展的虚拟机运行参数,其中对Hotspot VM相关的参数是我们进行性能调整的重点。


参数

使用说明

备注

-Xmixed

JVM执行模式的设置参数,混合模式即支持Hotspot即时编译的运行模式

支持HotspotJVM缺省都是运行于混合模式的。

-Xint

设置JVM的执行模式为解释执行模式,纯解释执行的JVM对多数应用来说基本上时没有意义的,仅仅可能会在一些嵌入式系统中应用

 

-Xbootclasspath

设置初始类装载器的装载路径

 

-Xnoclassgc

设置不执行类垃圾收集

 

-Xincgc

设置是否启动火车垃圾收集算法

 

-Xloggc:<file>

设置是否将GC信息写入日志文件

 

-Xbatch

设置不执行后台编译

 

-Xms<size>

设置JVM启动时初始内存堆的大小

 

-Xmx<size>

设置JVM启动后动态申请堆内存的最大堆空间

 

-Xss<size>

设置JVM最大线程栈的空间大小

 

-Xprof

是否打印输出性能统计数据

 

-Xrunhprof

设置是否启动heapcpu等性能统计监控功能(详细见下表)

 

-Xdebug

设置是否启动远程调试功能

 

-Xfuture

 

 

-Xrs

设置是否屏蔽操作系统信号

 

-Xcheck:jni

设置对于本地调用是否执行额外检查

 


        1. JVM扩展参数集


Java HotspotGC相关参数介绍,下面以Solaris1.4.2版本为例,对于以–X打头的非标准参数,是不能保证在每个JVM的实现中都支持的,而且关于这些参数行为的改变都不会得到通知。


12.3.  非稳定(Stable)参数(-XX


而对于以–XX打头的非标准参数来说,它们中大多数都是和具体的操作系统支持有关的,而且有些甚至需要特殊的系统访问权限,而且这些参数也是遵循上述的改变不通知原则的。在使用中需要特别注意。


Sun的文档中,又分为三类:


行为参数Behavioral Options):用于改变jvm的一些基础行为;
性能调优Performance Tuning):用于jvm的性能调优;
调试参数Debugging Options):一般用于打开跟踪、打印、输出等jvm参数,用于显示jvm更加详细的信息;


12.3.1. 行为参数


 


参数及其默认值

描述

-XX:-DisableExplicitGC

禁止调用System.gc();但jvmgc仍然有效

-XX:+MaxFDLimit

最大化文件描述符的数量限制

-XX:+ScavengeBeforeFullGC

新生代GC优先于Full GC执行

-XX:+UseGCOverheadLimit

在抛出OOM之前限制jvm耗费在GC上的时间比例

-XX:-UseConcMarkSweepGC

对老生代采用并发标记交换算法进行GC

-XX:-UseParallelGC

启用并行GC

-XX:-UseParallelOldGC

Full GC启用并行,当-XX:-UseParallelGC启用时该项自动启用

-XX:-UseSerialGC

启用串行GC

-XX:+UseThreadPriorities

启用本地线程优先级


 


上面表格中黑体的三个参数代表着jvmGC执行的三种方式,即串行、并行、并发;


串行(SerialGCjvm的默认GC方式,一般适用于小型应用和单处理器,算法比较简单,GC效率也较高,但可能会给应用带来停顿;


并行(ParallelGC是指GC运行时,对应用程序运行没有影响,GCapp两者的线程在并行执行,这样可以最大限度不影响app的运行;


并发(ConcMarkSweepGC是指多个线程并发执行GC,一般适用于多处理器系统中,可以提高GC的效率,但算法复杂,系统消耗较大;


12.3.2. 性能调优


 


参数及其默认值

描述

-XX:LargePageSizeInBytes=4m

设置用于Java堆的大页面尺寸

-XX:MaxHeapFreeRatio=70

GCjava堆中空闲量占的最大比例

-XX:MaxNewSize=size

新生成对象能占用内存的最大值

-XX:MaxPermSize=64m

老生代对象能占用内存的最大值

-XX:MinHeapFreeRatio=40

GCjava堆中空闲量占的最小比例

-XX:NewRatio=2

新生代内存容量与老生代内存容量的比例

-XX:NewSize=2.125m

新生代对象生成时占用内存的默认值

-XX:ReservedCodeCacheSize=32m

保留代码占用的内存容量

-XX:ThreadStackSize=512

设置线程栈大小,若为0则使用系统默认值

-XX:+UseLargePages

使用大页面内存


 


12.3.3. 调试/打印信息参数


参数及其默认值

描述

-XX:-CITime

打印消耗在JIT编译的时间

-XX:ErrorFile=./hs_err_pid<pid>.log

保存错误日志或者数据到文件中

-XX:-ExtendedDTraceProbes

开启solaris特有的dtrace探针

-XX:HeapDumpPath=./java_pid<pid>.hprof

指定导出堆信息时的路径或文件名

-XX:HeapDumpOnOutOfMemoryError

当首次遭遇OOM时导出此时堆中相关信息

-XX:OnError="<cmd args>;<cmd args>"

出现致命ERROR之后运行自定义命令

-XX:OnOutOfMemoryError="<cmd args>;<cmd args>"

当首次遭遇OOM时执行自定义命令

-XX:-PrintClassHistogram

遇到Ctrl-Break后打印类实例的柱状信息,与jmap -histo功能相同

-XX:-PrintConcurrentLocks

遇到Ctrl-Break后打印并发锁的相关信息,与jstack -l功能相同

-XX:-PrintCommandLineFlags

打印在命令行中出现过的标记

-XX:-PrintCompilation

当一个方法被编译时打印相关信息

-XX:-PrintGC

每次GC时打印相关信息

-XX:-PrintGC Details

每次GC时打印详细信息

-XX:-PrintGCTimeStamps

打印每次GC的时间戳

-XX:-TraceClassLoading

跟踪类的加载信息

-XX:-TraceClassLoadingPreorder

跟踪被引用到的所有类的加载信息

-XX:-TraceClassResolution

跟踪常量池

-XX:-TraceClassUnloading

跟踪类的卸载信息

-XX:-TraceLoaderConstraints

跟踪类加载器约束的相关信息


 


 


12.3.4. 参数汇总(不含调试和垃圾收集参数)


 


参数

使用说明

备注

-XX:+AggressiveHeap

长时间大内存使用的优化,能检查计算资源(内存,处理器数量),至少需要256MB内存大量的CPU/内存,(在1.4.14CPU的机器上已经显示有提升)

 

-XX:+AggressiveOpts

加快编译

 

-XX:-AllowUserSignal

Handlers

允许用户在应用层设置信号处理回调函数

 

-XX:AltStackSize=16384

预备信号栈的大小

 

-XX:-CITime

设置Hotspot的一次即时编译所需要的最大时间

 

-XX:CompileThreshold

=10000

设置方法是否进行即时编译的调用次数的下限值,-server选项的缺省值为10000-client选项的缺省值为1500

即:当该方法的被调用测试多于该值时,则该方法就会被JIT即时编译器编译成机器代码在内存中执行

-XX:+DisableExplicitGC

屏蔽程序主动垃圾收集的函数system.gc()

 

-XX:FreqInlineSize=size

限制经常使用的动态编译的函数的虚拟机指令的最大数量,

 

-Xincgc

在垃圾收集中使用火车算法

 

-Xint

不启用即时编译(JIT)功能,仅仅解释执行

缺省为不选的

-XX:LargePageSizeInBytes

内存页的大小,不可设置过大,会影响Perm的大小。

 

 

 

 

-XX:MaxHeapFreeRatio

=<Maximum>

JVM中堆空间的最大空闲百分比,缺省为70%GC停止回收空间的上限值

即:一旦当前堆内存空闲空间百分比超过总空间70%时,GC暂停垃圾收集

-XX:MinHeapFreeRatio

=<Minimum>

JVM中堆空间的最小空闲百分比,缺省为40%GC开始回收空间的下限值

即:一旦当前内存堆中内存空闲小于40%时,GC则恢复垃圾收集

-XX:MaxInlineSize=size

限制动态编译的内联函数的虚拟机指令的最大数量

 

-XX:+MaxFDLimit

设置JVM进程打开最大文件句柄数(Solaris only

 

-XX:MaxNewSize=32m

在为新生代对象分配内存值,每块内存的最大值。

按代垃圾收集中使用

-XX:MaxTenuringThreshold=30

在存活区之间Copy的次数,超过该次数则移至Old区。

 

-Xmn

为新生代分配的内存大小。

 

-XX:NewRatio=2

新生代与老一代空间的比率,-XX:NewRatio = 2表示 Eden:old = 1:2SUN Parc –server中是21Intel中是121

 

-XX:NewSize=2228224

新一代的缺省申请空间的值

对于大型应用服务器系统这个值2K一般情况下需要调整大一些

-Xnoincgc

在垃圾收集中不使用火车算法

 

-XX:PreBlockSpin=10

 

 

-XX:-PrintTenuring

Distribution

打印使用年限

 

-XX:ReservedCodeCache

Size=32m

设置内存中保留代码缓冲区的大小

 

-XX:SoftRefLRUPolicyMSPerMB

相对于客户端模式的虚拟机(-client选项),当使用服务器模式的虚拟机时(-server选项),对于软引用(soft reference)的清理力度要稍微差一些。可以通过增大-XX:SoftRefLRUPolicyMSPerMB来降低收集频率。默认值是 1000,也就是说每秒一兆字节。Soft reference在虚拟机中比在客户集中存活的更长一些。其清除频率可以用命令行参数-XX:SoftRefLRUPolicyMSPerMB=<N>来控制,这可以指定每兆堆空闲空间的 soft reference保持存活(一旦它不强可达了)的毫秒数,这意味着每兆堆中的空闲空间中的 soft reference会(在最后一个强引用被回收之后)存活1秒钟。注意,这是一个近似的值,因为 soft reference只会在垃圾回收时才会被清除,而垃圾回收并不总在发生。

 

-XX:SurvivorRatio=64

存活区和eden区所占的比率:2:64

 

-XX:TargetSurvivorRatio

=50

该值是一个百分比,控制允许使用的生存区空间的比例,默认值是50。即占到50%,则执行Copy策略。该参数设置较大的话可提高对survivor空间的使用率。

 

-XX:ThreadStackSize

=512

每个线程栈大小(K),等于0时表示使用缺省值【Sparc: 512KSolaris Intel: 256KSparc 64bit: 1024其他的都为0

 

-XX:+UseBoundThreads

绑定用户级线程(Solaris only),这个选项强制所有的Java线程在创建时都作为操作系统绑定的线程

这个参数用来是否将JVM用户线程绑定到Solaris内核线程

-XX:+UseAltSigs

 

 

-XX:+UseV8InstrsOnly

 

 

-XX:-UseLWPSynchroniza

tion

使用操作系统提供的轻量级线程LWP同步来代替基于Java虚拟机的线程的同步

该参数的使用使得JVM将线程同步的控制交由Solaris内核处理,从而代替了JVM内部的线程同步机制

-XX:+UseThreadPriorities

设置是否使用本地线程优先级

 

-XX:-UseSpinning

 

 

-XX:+UseTLAB

是否使用线程本地对象分配策略,SUN Sparc –server时为true,其他为false

 

-XX:-UseISM

如果使用ISM选项可以获得如下的几个好处:1、使用大内存页来代替操作系统缺省的8K的页模式;

2、将一些内存页锁定在内存中,而不必换出到硬盘

 

如果系统使用ISM则系统文件/etc/system需要添加如下配置:

set shmsys:

shminfo_shmmax

=0xffffffff

set shmsys:

shminfo_shmseg=32

-XX:+UseFastAccessorMethods

原始类型的快速优化,get,set方法转成本地代码。

 

-XX:+UseBiasedLocking

锁机制的性能改善。

 


        1. JVM GC/Hotspot相关参数集


注:即时编译是Hotspot中的概念,按代收集,火车算法等是属于GC中的概念。


 


13.   项目调优经验1


  1. CMS垃圾收集器

    Date2010-12-24

    JVM参数:


-Xss2M -XX:PermSize=128M -XX:MaxPermSize=128M -Xms2G -Xmx2G -Xmn832M -XX:SurvivorRatio=6 -XX:TargetSurvivorRatio=80 -XX:ParallelGCThreads=8 -XX:+UseConcMarkSweepGC


观察分析:


  1. 新生代每次执行一次垃圾收集,旧生代增加一点:


旧生代:489,711 Kbà489,740Kb ;


新生代(638,976kb)651,878Kbà29,00kb


存活区(106,496kb):10,000kbà5,000kb


 


上述数据说明:对象在存活只存活一个周期,就被移到旧生代;相对应新生代每次收集存活区的最高利用率只有10%,根据上述参数可以把存活区Copy次数改为:(新生代分配值/存活区最大利用值)*目标存活区利用率=100M/10M*80%=8


 


  1. 垃圾收集数据

    新生代收集:ParNew29388次,用时:13m

    旧生代:ConurrentMarkSweep:0次,用时0s

     

    新生代平均每次收集暂停时间为:0.027s(平均每次用时:13m/29388=0.027s)。

    新生代收集29388次,旧生代0次:旧生代内存利用率远远低于新生代,可以适当放大旧生代,缩小新生代。

     

  2.  

     

    结论:扩大新生代内存,缩小旧生代内存,相应提升并发收集线程数量;增加-XX:MaxTenuringThreshold参数,设置值为8,要求对象在存活区之间Copy 10次在移动到旧生代。

     


  1. 并行垃圾收集自适应调整堆内存

     


**管控项目:64windows服务器


jvm 64: jdk1.5.0_16_b02


上线时间:2010.10,出现jvm不响应时间:2010.12.16


启动参数:


-server-Xss2M -XX:PermSize=64M -XX:MaxPermSize=64M


-XX:+UseParallelGC-XX:+UseAdaptiveSizePolicy -XX:+AggressiveHeap -XX:ParallelGCThreads=4


说明:采用并行垃圾回收器,堆内存通过JVM自适应调整(-XX:+UseAdaptiveSizePolicy-XX:+AggressiveHeap)。


 


问题:2010.12.16项目组发现运行在jvm上的引擎服务不响应,经分析是jvm在做垃圾全收集时,对jvm进行了一次自动重启。


 


方案:采用并发收集器,内存参数固定”,手动执行垃圾收集后,不出现jvm重启现象。


 


  1. 采用并发收集,内存固定


 


**管控项目:64windows服务器


jvm 64: jdk1.5.0_16_b02


 


启动参数:


-server-Xss2M -XX:PermSize=64M -XX:MaxPermSize=64M -Xms2G -Xmx2G -Xmn512M


-XX:SurvivorRatio=16-XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=10 -XX:ParallelGCThreads=4


 


分析:旧生代采用并发垃圾收集器(CMS)管理;新生代采用并行垃圾收集器(Parall);内存参数固定。


 


正常运行时间

1 hour 4 minutes

处理 CPU时间

6 minutes

JIT编译器

HotSpot 64-Bit Server Compiler

编译总时间

1 minute


 


垃圾收集器

Name = 'ParNew', Collections = 158, Total time spent = 9.392

垃圾收集器

Name = 'ConcurrentMarkSweep', Collections = 1, Total time spent = 2.076


 


 


 


14.   调优经验(摘抄)


14.1.  GC调优参数的使用


JVM中按代收集算法的基本原则是这样的,JVM堆空间被分成许多子堆,每个子堆用于存放不同代的对象,而当所有已经存在的堆中的各代对象都不能继续回收时,则新的子堆会被分配,用于存放新一代的对象,下面的这两个参数就是为按代收集设计的:


-XX:NewRatio=2              //新生代于老一代的空间比率


-XX:NewSize=2228224           //缺省时2K,对于应用服务器系统建议调整的大一些4K8K


-XX:MaxNewSize                    //新生代的最大空间


-XX:MaxPermSize=64m         //老一代的最大空间,缺省为64M,建议增加


-XX:SurvivorRatio                  //GC时代子堆中的年老对象的比率


-Xxincgc                           //是否在垃圾收集时启用火车算法


-XX:+UseConcMarkSweepGC//是否启用并发收集算法


-Xverifyheap              //仅仅用于Debug版本,用于对GC数据分析


-XX:TargetSurvivorRatio=50   //GC收集后期望得到的被老一代占用的空间,建议不调整


 


因为垃圾收集只是在各代的子堆满了的时候发生,总的堆的空间状况也会对垃圾收集产生重要的影响,JVM向操作系统申请更多的堆内存空间的前提是,堆中所有的年老的代的子堆都已经满了。


-Xms                          //设置最小初始堆空间的大小


-Xmx                          //设置最大堆空间的大小


-XX:MinFreeHeapRatio=40     //GCJVM堆空间向操作系统缩小的比率。


-XX:MaxHeapFreeRatio70  //GCJVM堆空间向操作系统扩张的比率。


-XX:+AggressiveHeap            //用于JVM运行于大内存模式下,JVM的堆空间至少在1G以上,与-Xms-Xmx不同时使用,慎用!会导致JVM极度消/耗内存


U-NICA这样的后台大型应用服务器系统来说,我们就采用了通过调整初始堆内存空间、堆增长量、增加新生代空间配置、使用并发收集算法、火车算法等方法来使的系统的垃圾收集能力得以优化。而且在具体实际测试过程中也发现这些调整通常是有效而且成本低廉的。


14.2.  JIT调优参数的使用


从解释执行到即时编译,再到热点编译JVM走在一条不断优化的道路上,今天我们通过一些简单的参数设置就可以获得以前梦想的性能,对于Java用户来说,这的确是一条捷径:


-server                       //很多Hotspot的能力都是通过这个选项打开的,对于大型服


//务器尤为重要,这里可以启动热点编译功能


-Xmaxjitcodesize32m       //设置即时编译代码的最大尺寸


-Xint                           //纯解释执行,一般情况下不用它


-Xtime                        //不太清楚如何使用,总之是指定JIT的时间


-XX:+DisableExplicitGC          //是否屏蔽应用层的垃圾收集请求


-XX:-UseISM                    //使用大内存页模式则会减少GC的时间


-XX:-UseMPSS                //在使用ISM选项的同时不使用该选项,否则ISM无效


14.3.  Java线程调优参数的使用


-XX:-UseBoundThreads          //如果你的系统是Solaris8以上,尽情使用这个参数吧,操作系统内核的线程调度总是要必应用层的调度快一些


-XX:-UseLWPSynchronization//这个参数也是同样的,让操作系统来做线程同步这些工作


-XX:+UseThreadPriorities       //是否采用操作系统内部定义的线程优先级


-XX:CompileThreshold=10000//如果你想让你的系统更早变快一些,而且你的内存足够多的话,可以将这个参数值调小


-XX:PreBlockSpin=10             //仅仅用于Linux版本


-XX:ThreadStackSize=512     //设置线程栈的大小,如果你的应用中有比较大的循环或递


归时使用。


-XX:+UseTLAB                //是否在线程的栈空间内分配对象,如果你的内存较大的话,并且配置了比较大的线程栈空间,则使用这个参数会使得临时&本地对象的申请和释放比较快。




猜你喜欢

转载自blog.csdn.net/wangshuminjava/article/details/80930434