http://blog.csdn.net/zhoutao198712/article/details/7783070java
到目前为止,尚未作明确的优化工做。只是作了初始化选择工做,好比说:JVM部署模型、JVM运行环境、收集哪些垃圾回收器的信息以及须要遵照垃圾回收原则。这一步将介绍如何评估应用须要的内存大小以及Java堆大小。首先须要判断出应用存活的数据的大小,存活数据的大小是决定配置应用须要的Java堆大小的重要条件,也可以决定是否须要从新审视一下应用的内存需求或者修改应用程序以知足内存需求。linux
注意:存活数据是指,应用处于稳定运行状态下,在Java堆里面长期存活的对象。换一句话说,就是应用在稳定运行的状态下,Full GC以后,Java堆的所占的空间。缓存
约束工具
有多少物理内存能够供JVM使用?是部署多个JVM或者单个JVM?对作出的决定有重要影响。下面列出了一些要点能够帮助决定有多少物理内存能够供使用。性能
一、一个机器上面只是部署一个JVM,且就一个应用使用?若是是这种状况,那么机器的全部物理内存能够供JVM使用。测试
二、一个机器上部署了多个JVM?或者一个机器上部署了多个应用?若是是这两个中的任何一种状况,你就必需要决定每个JVM或者应用须要分配多少内存了。优化
不管是前面的哪一种状况,都须要给操做系统留出一些内存。spa
HotSpot VM的堆结构操作系统
在作内存占用测量以前,咱们必需要先理解HotSpot VM Java堆的结构,理解这个对决定应用须要的Java堆大小以及优化垃圾收器性能有很好的帮助。.net
HotSpot VM有3个主要的空间:young代、old代以及permanent代,如上图所示。
当Java应用分配Java对象时,这些对象被分配到young代。在经历过几回minor GC以后,若是对象仍是存活的,就会被转移到old代空间。permanent代空间存储了VM和Java类的元数据好比内置的字符串和类的静态变量。
-Xmx和-Xms这个两个命令行选项分别指定yound代加上old代空间的总和的初始最大值和最小值,也就是Java堆的大小。当-Xms的值小于-Xmx的值的时候,Java堆的大小能够在最大值和最小值以前浮动。当Java应用强调吞吐量和延迟的时候,倾向于把-Xms和-Xmx设置成相同的值,因为调整young代或者old代的大小都须要进行Full GC,Full GC下降吞吐量以及增强延迟。
young代的空间能够经过下面的任意一个命令行选项来设置:
young代的初始值和最小值。<n>是大小,[g|m|k]表示单位是G字节,M字节或者千字节。young代的大小不会小于这个值。当设定-XX:NewSize=<n>[g|m|k]的时候,-XX:MaxNewSize=<n>[g|m|k]须要被指定。
young区空间的最大值。同上面反过来,当指定-XX:MaxNewSize=<n>[g|m|k]的须要指定-XX:NewSize=<n>[g|m|k]。
直接指定young代的初始值、最小值以及最大值。也就是说,young区的大小被固定成这个值了。这个值用来锁定young代的大小很方便。
有一点须要注意的是,若是-Xms和-Xmx没有被设定成相同的值,并且-Xmn被使用了,当调整Java堆的大小的时候,不会调整young代的空间大小,young代的空间大小会保持恒定。所以,-Xmn应该在-Xms和-Xmx设定成相同的时候才指定。
old代的空间大小能够基于young代的大小进行计算,old代的初始值的大小是-Xms的值减去-XX:NewSize,最大值是-Xmx减去-XX:MaxNewSize,若是-Xmx和-Xms设置成了相同的值,并且使用-Xmn选项或者-XX:NewSize和-XX:MaxNewSize设置成了相同的值,那么old代的大小就是-Xmx减去-Xmn。
permanent代的大小经过下面命令行参数指定
表示permanent代的初始值和最小值,n表示数值,g|m|k表示单位、permanent的空间必定不会比这个空间小。
permanent代的最大值,permanent代的大小不会超过这个值。
Java应用应该指定这两个值成为同一个值,因为这个值的调整会致使Full GC。
若是上面提到的Java堆大小、young代、permanent代的大小都没有指定,那么JVM会根据应用的状况自行计算。
在young代、old代以及permanent代中任何一个空间里面没法分配对象的时候就会触发垃圾回收,理解这点,对后面的优化很是重要。当young代没有足够空间分配Java对象的时候,触发minor GC。minor GC相对于Full GC来讲会更短暂。
一个对象在经历过必定次数的Minor GC以后,若是还存活,那么会被转移到old代(对象有一个“任期阀值”的概念,优化延迟的时候再介绍)。当old代没有足够空间放置对象的时候,HotSpot VM触发full GC。实际上在进行Minor GC的时候发现没有old代足够的空间来进行对象的转移,就会触发FullGC,相对于在MinorGC的过程当中发现对象的移动失败了而后触发FullGC,这样的策略会有更小的花费。当permanent代的空间不够用的时候的,也会触发FullGC。
若是FullGC是因为old代满了而触发的,old代和permanent代的空间都会被垃圾回收,即便permanent代的空间尚未满。同理,若是FullGC是因为permanent代满了而触发的,old代和permanent代的空间都会被垃圾回收,即便old代的空间尚未满。另外,young代一样会被垃圾回收,除非-XX:+ScavengeBeforeFullGC选项被指定了,-XX:+ScavengeBeforeFullGC关闭FullGC的时候young代的垃圾回收。
堆大小优化的起点
为了进行Java堆大小的优化,一个合适的起点很重要。这节描述的方案是须要先使用比应用须要量更大的Java堆做为开始。这一步的目的是收集一些初始化信息以及为了进一步优化Java堆大小须要的数据。
就像在“选择JVM runtime”小节里面提到过的,由吞吐量垃圾回收器(throughput garbage collector)开始。记住,使用吞吐量垃圾回收器经过设置-XX:+UserParallelOldGC命令行选项,若是你使用的HotSpot VM不支持的这个选项,那么就使用-XX:+UserParallelGC。
若是你可以准确的预估到应用须要消耗的Java堆空间,能够经过设定-Xmx和-Xms来做为这个步骤的起点。若是你不知道该设定什么值,就让JVM来选择吧,反正后面,都会根据实际状况进行优化调整。
关于如何监控GC日志前面的“GC优化基础”已经描述过了。GC日志会展现在使用中的java堆的大小。初始化和最大的堆大小能够经过-XX:+PrintCommandLineFlags来查看。-XX:+PrintCommandLineFlags打印出在HotSpot VM初始化的时候选择的初始值和最大值好比-XX:InitialHeapSize=<n> -XX:MaxHeapSize=<m>,这里n表示初始化的java堆大小值,m表示java堆的最大值。
无论你是指定java堆的大小仍是使用默认的大小,必须让应用进入稳定运行的状态,你必需要有能力和手段让应用处于和线上稳定运行的状态相同的状态。
若是在企图让应用进入稳定状态的时候,你在垃圾回收日志里面观察到OutOfMemoryError,注意是old代溢出仍是permanent代溢出。下面一个old代溢出的例子:
上面重要的部分加粗标示了,因为使用的是吞吐量垃圾回收器,old代的统计信息标示为ParOldGen。这行表示了old代的在FullGC的时候占用的空间。从这个结果来看,能够得出的结论是old代的空间过小了,因为FullGC先后old代的被占用的空间和分配的空间基本相等了,所以,JVM报了OutOfMemoryError。相比较,经过PSPermGen这行能够看出permanent代的空间占用是32390K,和他的容量(65536K)比仍是有必定的距离。
下面的例子展现了因为permanent太少了而致使的OutOfMemoryError发生的例子
同上面同样,把关键行标示出来了,经过PSPermGen这行能够看出在FullGC先后,他的空间占用量都和他的容量相同,能够得出的结论是permanent代的空间条小了,这样就致使了OutOfMemoryError。在这个例子里面,old的占用空间(132538K)远比他的容量(350208K)小。
若是在垃圾回收日志中观察到OutOfMemoryError,尝试把Java堆的大小扩大到物理内存的80%~90%。尤为须要注意的是堆空间致使的OutOfMemoryError以及必定要增长空间。好比说,增长-Xms和-Xmx的值来解决old代的OutOfMemoryError,增长-XX:PermSize和-XX:MaxPermSize来解决permanent代引发的OutOfMemoryError。记住一点Java堆可以使用的容量受限于硬件以及是否使用64位的JVM。在扩大了Java堆的大小以后,再检查垃圾回收日志,直到没有OutOfMemoryError为止。
若是应用运行在稳定状态下没有OutOfMemoryError就能够进入下一步了,计算活动对象的大小。
计算活动对象的大小
就像前面提到的,活动对象的大小是应用处于稳定运行状态时,长时间存活数据占用的Java堆的空间大小。换句话说,就是应用稳定运行是,在FullGC以后,old代和permanent代的空间大小。
活动对象的大小能够经过垃圾回收日志查看,它提供了一些优化信息,以下:
一、应用处于稳定运行状态下,old代的Java堆空间占用数量。
二、应用处于稳定运行状态下,permanent代的Java堆空间占用数量。
为了保证可以准确的评估应用的活动对象大小,最好的作法是多看几回FullGC以后Java堆空间的大小,保证FullGC是发生在应用处于稳定运行的状态。
若是应用没有发生FullGC或者发生FullGC的次数不多,在性能测试环境,能够经过Java监控工具来触发FullGC,好比使用VisualVM和JConsole,这些工具在最新的JDK的bin目录下能够找到,VisualVM集成了JConsole,VisualVM或者JConsole上面有一个触发GC的按钮。
另外,jmap命令能够选择来强制HotSpot VM进行FullGC。jmap 须要-histo:live命令选项以及JVM进程id。JVM的进程id能够经过jps命令获取。好比JVM的进程id是348,jmap命令用来触发FullGC能够想以下这样写:
jmap不只仅触发FullGC,并且产生堆的关于对象分配的概要信息。不过就目前这步的目的而言,能够忽略产生的堆概要信息。
初始化堆大小配置
本节描述了怎样利用活动对象的大小来决定初始化的Java堆的大小。下面的图,给出了应用存活的对象的大小。比较明智的作法是多收集几回FullGC信息,有更多的信息,可以作出更加好的决定。
经过活动对象大小的信息,能够作出关于Java堆的大小有根据的决定,以及能够估计出最坏状况下会致使的延迟。
比较常规是,Java堆大小的初始化值和最大值(经过-Xms和-Xmx选项来指定)应该是old代活动对象的大小的3到4倍。
在上图中显示的FullGC信息中,在FullGC以后old代的大小是295111K,差很少是295M,即活动的对象的大小是295M。所以,推荐的Java堆的初始化和最大值应该是885M到1180M,便可以设置为-Xms885m -Xmx1180m。在这个例子中,Java堆的大小是1048570K差很少1048M,在推荐值范围内。
另一个常规是,permanent的初始值和最大值(-XX:PermSize和-XX:MaxPermSize)应该permanent代活动对象大小的1.2到1.5倍。在上图中看到在FullGC以后permanent代占用空间是32390K,差很少32M。所以,permanent代的推荐大小是38M到48M,便可以设置为-XX:PermSize=48m -XX:MaxPermSize=48m(1.5倍)。这个例子里面,permanent代的空间大小是65536K即64M,大出了17M,不过在1G内存的系统的中,这个数值彻底能够忍受。
另一个常规是,young代空间应该是old代活动对象大小的1到1.5倍。那么在这里例子中,young代的大小能够设置为295M到442M。本例里面,young代的空间大小的358400K,差很少358M,在推荐值中间。
若是推荐的Java堆的初始值和最大值是活动对象大小3到4倍,而young代的推荐只是1到1.5倍,那么old代空间大小应该是2到3倍。
经过以上规则,咱们可使用的Java命令能够是这样的:
另一些考虑
本节将说起到在进行应用内存占用评估的时候,另一些须要记住的点。首先,必需要知道,前面只是评估的Java堆的大小,而不是Java应用占用的全部的内存,若是要查看Java应用占用的全部内存在linux下能够经过top命令查看或者在window下面经过任务管理器来查看,尽管Java堆的大小可能对Java应用占用内存作出了最大的贡献。 好比说,为了存储线程堆栈,应用须要额外的内存,越多的线程,越多内存被线程栈消耗,越深的方法间调用,线程栈越多。另外,本地库须要分配额外的内存,I/O缓存也须要额外的内存。应用的内存消耗须要评估到应用任何一个会消耗内存的地方。
记住,这一步操做不必定可以知足应用内存消耗的需求,若是不能知足,就回过头来看需求是否合理或者修改应用程序。比较可行的一种办法是修改应用程序减少对象的分配,从而减小内存的消耗。
Java堆的大小计算仅仅只是开始,根据需求,在后面的优化步骤中可能会修改。