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

JVm性能调优4:GC日志分析 通过分析gc日志,分析系统瓶颈。 阅读详情


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

本系列包括:


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

1.      内存结构


1.1.     分代结构图


注意:


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


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


-client下:非堆包括:持久代、代码缓存(Code cache)、Perm rw和perm 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.) (from《Tuning 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). (from《Tuning 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 .(from《Tuning GC with jdk 1.5》)


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


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


3.      引用类型


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


3.1.     强引用


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


3.2.     软引用


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


3.3.     弱引用


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


3.4.     虚引用


 


4.      内存分配回收次序


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


新生区又分为两部分:伊甸区(Eden space)和幸存者区(Survivorpace),所有的类都是在enden被new出来的。幸存区有两个: 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.1,1.2,1.3,1.4,2.1,2.2,2.3,3.1,3.2,3.3。这个顺序也是块被创建的先后顺序。


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


如图二,车厢1.1中有对象A和对象B,1.3中有对象C,1.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,即火车的尾部)


图三


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


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


 


 


7.      触发垃圾收集的条件


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


7.1.     次收集ScavengeGC


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


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


7.2.     全收集FullGC


对整个堆进行整理,包括Young、Tenured和Perm。Full 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 Survivor和To 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以及其后继版本来看,即使是对于那些只有4颗CPU的       主机,该选项都是很有帮助的。因此,吞吐收集器(-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:+UseParallelGC和XX:+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 gc。gc 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。在我的应用中Xmx是6000,Xmn是500,那么Xmx-Xmn是5500兆,也就是老年代有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以前默认是256K,1.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


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


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


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


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


 


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


12.1.  标准参数(-)


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


参数

使用说明

备注

-d32

-d64

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

因为64的CPU兼容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虚拟机非标准参数指的就是一些特有的虚拟机实现所支持,下面以Solaris下1.4.2版本为例介绍一些扩展的虚拟机运行参数,其中对Hotspot VM相关的参数是我们进行性能调整的重点。


参数

使用说明

备注

-Xmixed

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

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

-Xint

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

 

-Xbootclasspath

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

 

-Xnoclassgc

设置不执行类垃圾收集

 

-Xincgc

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

 

-Xloggc:<file>

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

 

-Xbatch

设置不执行后台编译

 

-Xms<size>

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

 

-Xmx<size>

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

 

-Xss<size>

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

 

-Xprof

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

 

-Xrunhprof

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

 

-Xdebug

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

 

-Xfuture

 

 

-Xrs

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

 

-Xcheck:jni

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

 


        1. JVM扩展参数集


Java Hotspot、GC相关参数介绍,下面以Solaris下1.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();但jvm的gc仍然有效

-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

启用本地线程优先级


 


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


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


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


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


12.3.2. 性能调优


 


参数及其默认值

描述

-XX:LargePageSizeInBytes=4m

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

-XX:MaxHeapFreeRatio=70

GC后java堆中空闲量占的最大比例

-XX:MaxNewSize=size

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

-XX:MaxPermSize=64m

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

-XX:MinHeapFreeRatio=40

GC后java堆中空闲量占的最小比例

-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.1在4CPU的机器上已经显示有提升)

 

-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:2。SUN Parc –server中是2:1,Intel中是12:1。

 

-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: 512K,Solaris Intel: 256K,Sparc 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垃圾收集器

    Date:2010-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. 并行垃圾收集自适应调整堆内存

     


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


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. 采用并发收集,内存固定


 


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


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,对于应用服务器系统建议调整的大一些4K或8K


-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     //GC后JVM堆空间向操作系统缩小的比率。


-XX:MaxHeapFreeRatio=70  //GC后JVM堆空间向操作系统扩张的比率。


-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 rw和perm 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.) (from《Tuning 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). (from《Tuning 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 .(from《Tuning GC with jdk 1.5》)


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


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


3.      引用类型


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


3.1.     强引用


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


3.2.     软引用


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


3.3.     弱引用


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


3.4.     虚引用


 


4.      内存分配回收次序


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


新生区又分为两部分:伊甸区(Eden space)和幸存者区(Survivorpace),所有的类都是在enden被new出来的。幸存区有两个: 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.1,1.2,1.3,1.4,2.1,2.2,2.3,3.1,3.2,3.3。这个顺序也是块被创建的先后顺序。


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


如图二,车厢1.1中有对象A和对象B,1.3中有对象C,1.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,即火车的尾部)


图三


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


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


 


 


7.      触发垃圾收集的条件


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


7.1.     次收集ScavengeGC


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


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


7.2.     全收集FullGC


对整个堆进行整理,包括Young、Tenured和Perm。Full 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 Survivor和To 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以及其后继版本来看,即使是对于那些只有4颗CPU的       主机,该选项都是很有帮助的。因此,吞吐收集器(-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:+UseParallelGC和XX:+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 gc。gc 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。在我的应用中Xmx是6000,Xmn是500,那么Xmx-Xmn是5500兆,也就是老年代有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以前默认是256K,1.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


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


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


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


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


 


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


12.1.  标准参数(-)


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


参数

使用说明

备注

-d32

-d64

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

因为64的CPU兼容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虚拟机非标准参数指的就是一些特有的虚拟机实现所支持,下面以Solaris下1.4.2版本为例介绍一些扩展的虚拟机运行参数,其中对Hotspot VM相关的参数是我们进行性能调整的重点。


参数

使用说明

备注

-Xmixed

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

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

-Xint

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

 

-Xbootclasspath

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

 

-Xnoclassgc

设置不执行类垃圾收集

 

-Xincgc

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

 

-Xloggc:<file>

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

 

-Xbatch

设置不执行后台编译

 

-Xms<size>

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

 

-Xmx<size>

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

 

-Xss<size>

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

 

-Xprof

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

 

-Xrunhprof

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

 

-Xdebug

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

 

-Xfuture

 

 

-Xrs

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

 

-Xcheck:jni

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

 


        1. JVM扩展参数集


Java Hotspot、GC相关参数介绍,下面以Solaris下1.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();但jvm的gc仍然有效

-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

启用本地线程优先级


 


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


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


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


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


12.3.2. 性能调优


 


参数及其默认值

描述

-XX:LargePageSizeInBytes=4m

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

-XX:MaxHeapFreeRatio=70

GC后java堆中空闲量占的最大比例

-XX:MaxNewSize=size

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

-XX:MaxPermSize=64m

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

-XX:MinHeapFreeRatio=40

GC后java堆中空闲量占的最小比例

-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.1在4CPU的机器上已经显示有提升)

 

-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:2。SUN Parc –server中是2:1,Intel中是12:1。

 

-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: 512K,Solaris Intel: 256K,Sparc 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垃圾收集器

    Date:2010-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. 并行垃圾收集自适应调整堆内存

     


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


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. 采用并发收集,内存固定


 


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


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,对于应用服务器系统建议调整的大一些4K或8K


-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     //GC后JVM堆空间向操作系统缩小的比率。


-XX:MaxHeapFreeRatio=70  //GC后JVM堆空间向操作系统扩张的比率。


-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
Spring Boot 项目优化和 JVM 调优,亲测!真实有效。。(2) 在这个部分总结了2019年到目前为止Java常见面试问题,取其面试核心编写成这份文档笔记,从中分析面试官的心理,摸清面试官的“套路”,可以说搞定90%以上的Java中高级面试没一点难度。本节总结的内容涵盖了:消息队列、Redis缓存、分库分表、读写分离、设计高并发系统、分布式系统、高可用系统、SpringCloud微服务架构等一系列互联网主流高级技术的知识点。(上述只是一个整体目录大纲,每个点里面都有如下所示的详细内容,从面试问题——分析面试官心理——剖析面试题——完美解答的一个过程) 阅读详情

相关推荐

Crazyflie 2.1固件刷写与地址配置全攻略:从开箱到连接Crazyswarm

本文详细介绍了Crazyflie 2.1微型无人机的固件刷写、地址配置及与Crazyswarm系统的集成方法。从开箱检查到多机协同飞行,涵盖了硬件准备、固件刷写、无线电地址修改等关键步骤,帮助用户快速实现集群飞行控制。特别针对Crazyswarm系统集成提供了实用配置示例和调试技巧。

weixin_42530458的博客 405

JVM调优详解(一次java性能优化实战)

本文章主要讲述什么是JVM虚拟机,JVM的内存组成,垃圾回收算法,不同的垃圾回收器,以及性能调优的参数,若对JVM组成已有充足的认识,可直接查看第四章节JVM调优具体参数讲解。本文主要对JVM虚拟机以及垃圾回收机制,性能调优做了一个较为全面的讲解,希望能对读者有所帮助,学无止境。作者:龙猫帝原文链接:https://juejin.cn/post/6958087856304685092/版权所有,欢迎保留原文链接进行转载:)

chang_mao的博客 1458

地平线旭日X3派(RDK X3)从开箱到联网:新手避坑指南

本文为地平线旭日X3派(RDK X3)新手提供从开箱到联网的完整避坑指南。详细讲解了硬件识别、系统镜像烧录、串口调试(波特率921600)、有线/无线网络配置以及SSH/VNC远程访问等关键步骤,帮助开发者快速搭建稳定的开发环境,为后续AI应用部署扫清障碍。

weixin_29174385的博客 955

性能优化实践总结——JAVA

从JVM、高并发、数据库、缓存、生成环境实践等多方面总结了Java应用程序的性能优化实践方案!

码说芯语的博客 3585

不用Root也能抓包?Android高版本HTTPS抓包的3种替代方案(含Charles技巧)

本文针对Android高版本HTTPS抓包难题,提供了三种无需Root权限的实战解决方案:利用VirtualXposed等虚拟沙盒环境、配置Postern等系统级代理工具,以及通过修改应用源码或网络安全配置。文章还详细解析了Charles抓包工具在非Root环境下的高级配置与排错技巧,帮助开发者在遵守安全规范的前提下,有效进行网络调试与监控。

http9protocoller的博客 648

Android音频混音器AudioMixer实战:从多路输入到设备播放的完整流程解析

本文深入解析了Android平台上音频混音器AudioMixer的完整工作流程,涵盖从多路音频输入、轨道创建与配置、AudioBufferProvider数据源接入,到核心混音处理与最终设备播放的全过程。文章重点探讨了如何高效管理音频数据流、配置音量与重采样参数,并提供了缓冲区管理与性能优化的实战技巧,帮助开发者构建稳定、低延迟的音频应用。

l3m4n的博客 342

笔记|Java性能优化实践 第9章

9.JVM 上的代码执行Java 虚拟机规范描述了符合规范的Java实现需要如何执行代码9.1 字节码解释概述JVM以栈式机器的方式运行;JVM提供的三个主要存储区•求值栈属于特定方法的本地•用于临时存储结果的局部变量(也属于方法本地)•对象堆在方法和线程间共享栈式计算的举例计算if x < 3 + 1的过程:初始状态:初始解释状态计算右侧子树,3进栈子树求值1继续进栈子树求值对...

程序空间 940

别再为OpenBCI_GUI安装发愁了!保姆级教程带你从Processing配置到成功运行(附常见错误解决)

本文提供了一份详细的OpenBCI_GUI安装教程,涵盖从Processing配置到成功运行的全过程,特别针对常见错误提供了解决方案。通过环境搭建、软件依赖安装、源码处理及首次运行实战,帮助用户高效完成数据可视化工具的部署与使用。

weixin_28366475的博客 272

十六.吊打面试官系列-JVM优化-JVM性能调优实战

在Java应用的开发和运维过程中,JVM的性能调优是一项至关重要的工作。随着业务的增长和复杂度的提升,线上问题排查、内存监控、参数优化以及压力测试成为每一位开发者和运维人员必须面对的挑战。本篇文章将带您走进JVM性能调优的世界,通过系统讲解线上问题的快速定位与解决、JVM内存的实时监控与分析、关键参数的精细调优策略以及如何进行有效的压力测试,让您掌握一套完整的JVM调优方法论。JVM调优一个很大的原则就是避免频繁的Full GC,导致Full GC的情况有下面这几种。

墨家巨子@俏如来 1749

Day470&471.Netty概述&Netty高性能架构设计 -netty

Netty概述 一、原生 NIO 存在的问题 NIO 的类库和 API 繁杂,使用麻烦:需要熟练掌握 Selector、ServerSocketChannel、SocketChannel、ByteBuffer等。 需要具备其他的额外技能:要熟悉 Java 多线程编程,因为 NIO 编程涉及到 Reactor 模式,你必须对多线程和网络编程非常熟悉,才能编写出高质量的 NIO 程序。 开发工作量和难度都非常大:例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常流的处理等等。 JDK NIO

阿昌爱Java 1574

【Bug已解决】Claude Code Auto-update failed 自动更新失败的解决方案

方案适用场景推荐指数清理残留重新安装已经损坏,需要快速恢复可用⭐⭐⭐⭐⭐修复目录权限权限不足导致更新反复失败⭐⭐⭐⭐⭐关闭自动更新网络/安全软件环境不稳定⭐⭐⭐切换原生安装方式想从根源规避 npm 更新问题⭐⭐⭐⭐自检修复脚本团队/服务器环境需要自动化恢复⭐⭐⭐报错的本质是自动更新过程中的文件替换操作因权限不足或被中断而处于不完整状态,而不是 Claude Code 本身的功能缺陷。遇到问题先清理残留文件再重新安装,不要在损坏状态上反复尝试修复;

一键难忘的博客 1万+

JVM性能调优实战

JVM调优调什么 JVM 调优是一个系统而又复杂的过程,但我们知道,在大多数情况下,我们基本不用去调整 JVM 内存分配,因为一些初始化的参数已经可以保证应用服务正常稳定地工作了。而且一般情况下,就算出现了,也是架构师级别的去处理。 实际上,JVM调优,调的是稳定,并不能带给你性能的大幅提升。服务稳定的重要性就不用多说了,保证服务的稳定,gc永远会是Java程序员需要考虑的不稳定因素之一。复杂和高并发下的服务,必须保证每次gc不会出现性能下降,各种性能指标不会出现波动。因此我们通过压测的方式来看一下对JVM

个人站点:javatv.net 2574

基于Simulink的超级电容快速响应特性建模与仿真

本文介绍了基于Simulink的超级电容快速响应特性建模与仿真方法。主要内容包括:超级电容的工作原理、一阶RC等效电路模型及其动态电压方程;通过Simulink搭建超级电容模型的两种方法(Simscape Electrical推荐方案和基础模块透明教学方案);仿真100A×100ms放电脉冲下的电压响应特性,并与锂电池进行动态对比;分析超级电容在混合储能系统中的功率分配优势。文章还提供了工程实践要点和扩展方向,如电压均衡、温度影响评估以及与DC-DC变换器集成等。通过建模实践,验证了超级电容在毫秒级响应、高

xiaoheshang_123的博客 99

JVM性能调优2:JVM性能调优参数整理

整理的基于jdk 1.6 jvm参数。

rodesad的博客 8400

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

JVM性能调优3:JVM_堆溢出分析过程和命令

jvm heap堆溢出 分析过程。

rodesad的博客 1509

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

JVM性能调优5:Heap堆分析方法

通过分析heap堆,分析jvm性能瓶颈。

rodesad的博客 2601

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

“金三银四”春招指南之“性能调优”:MySQL+Tomcat+JVM,看完还怕面试官的轰炸

在面试前我整理归纳了一些面试学习资料,文中结合我的朋友同学面试美团滴滴这类大厂的资料及案例由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!大家看完有什么不懂的可以在下方留言讨论也可以关注。《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!有什么不懂的可以在下方留言讨论也可以关注。《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

2401_84048832的博客 1044

[JVM教程与调优] 为什么要学习JVM虚拟机?,阿里一面面试官

在面试前我整理归纳了一些面试学习资料,文中结合我的朋友同学面试美团滴滴这类大厂的资料及案例由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!大家看完有什么不懂的可以在下方留言讨论也可以关注。网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)一个人可以走的很快,但一群人才能走的更远!

2401_84411956的博客 1098

[JVM教程与调优] 为什么要学习JVM虚拟机?,腾讯、字节跳动面经已发

ty与RPC、网络、日志、Zookeeper、Kafka、RabbitMQ、Hbase、MongoDB 、Cassandra、设计模式、负载均衡、数据库、一致性算法 、JAVA算法、数据结构、算法、分布式缓存、Hadoop、Spark、Storm的大量技术点且讲解的非常深入**面试前做足功夫,让你面试成功率提升一截,这里一份热门350道一线互联网常问面试题及答案助你拿offer。

2301_78416543的博客 1011

上线 1 小时突破 10W 下载,这本《Java 性能调优实战》真的有这么牛吗?

JDK 是 Java 语言的基础库,熟悉 JDK 中各个包中的工具类,可以帮助你编写出高性能代码。这里我会从基础的数据类型讲起,涉及容器在实际应用场景中的调优,还有现在互联网系统架构中比较重要的网络通信调优。另一个是调优过程标准,带你了解通过哪些严格的调优策略,我们可以排查性能问题,从而解决问题。由于内容较多,本次将展示部分,如果看得不过瘾想更加深入地了解本笔记彻底掌握 Java 后端性能调优点击文末名片,扫码即可免费获取!以上是字节二面的一些问题,面完之后其实挺后悔的,没有提前把各个知识点都复习到位。

2401_84003690的博客 795

上线 1 小时突破 10W 下载 , 这本《Java 性能调优实战》真的有这么牛吗?

看完美团、字节、腾讯这三家的一二三面试问题,是不是感觉问的特别多,可能咱们真的又得开启面试造火箭、工作拧螺丝的模式去准备下一次的面试了。开篇有提及我可是足足背下了Java互联网工程师面试1000题,多少还是有点用的呢,换汤不换药,不管面试官怎么问你,抓住本质即可!能读到此处的都是真爱Java互联网工程师面试1000题。

2301_79058522的博客 1067

JVM参数及调优_jvm调优,开发这么久这些问题都不会

XX:MaxGCPauseMillis=n,最大GC停顿时间,这是个软目标,JVM将尽可能(但不保证)停顿小于这个时间,这个时间不能设置太大,如果设置10秒,就意味着程序10秒的时间程序都不在运行。本来停顿1秒,结果你设置1ms,GC也做不到,它只能尽量的调整新生代,老年代,survivior等的尽量靠拢你设置的时间。启动程序,模拟用户请求,每100毫秒钟创建1000线程,每个线程创建一个512kb的对象,最多100毫秒内同时存在1000线程,并发量1000/s,吞吐量6000/s,查看GC的情况。

2401_84267675的博客 939
上一篇: 每天进步一点点——五分钟理解一致性哈希算法(consistent hashing)
下一篇: jstack(查看线程)、jmap(查看内存)和jstat(性能分析)命令
王树民
博客等级 码龄10年 912粉丝 560原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值