JVM为垃圾收集器(GC)、堆(Heap)大小和运行时编译器(Runtime Compiler)提供了与平台相关的默认参数。这些参数符合不同类型应用程序的需求,同时调优时只需要输入较少的命令。此外,基于行为的调优可以动态优化堆的大小,以满足应用程序的特定行为。
默认参数
比较重要的垃圾收集器,堆大小,编译器默认参数:
1.GC:garbage first (G1)收集器
2.线程数:GC线程的最大数量受到堆大小和可用CPU资源的限制
3.初始堆大小:初始堆大小为物理内存的1/64
4.最大堆大小:最大堆大小为物理内存的1/4
5.编译器:使用C1和C2的分层编译器(启动时使用C1编译器模式,一旦程序被适当预热,使用C2编译器模式,提供更主动的优化)
基于行为调优
通常情况下,垃圾收集器(GC)调优时会从吞吐量优先和暂停时间优先中选择一种作为目标。如果满足了首选目标,收集器将尝试最大化另一个目标。但很难同时满足所有的目标:应用程序需要一个最小的堆来容纳所有的活动数据,但预期目标还可能受其他配置影响。
暂停时间优先
暂停时间是垃圾收集器停止应用程序并恢复不再使用的空间的持续时间。暂停时间优先的目标是限制这些暂停的最长时间,提高程序的响应速度。
垃圾收集器记录平均暂停时间和平均值上的方差。平均数是从程序执行的开始算起的,但是它被加权了,所以最近的暂停计数权重更高。如果暂停时间的平均值加上方差大于最大暂停时间目标,则垃圾收集器认为目标没有被满足。
通过命令行选项-XX:MaxGCPauseMillis=<nnn>来指定程序最大暂停时间,垃圾收集器会收到相应的提示:暂停时间为<nnn>毫秒或者更少。垃圾收集器调整Java堆大小和其他与垃圾收集相关的参数,试图保持垃圾收集暂停时间小于<nnn>毫秒。最大暂停时间的缺省值因收集器的不同而不同。这些调整可能导致更频繁地进行垃圾收集,从而降低应用程序的总体吞吐量。在某些情况下,期望的暂停时间目标无法实现。
吞吐量优先
吞吐量目标是根据收集垃圾所花费的时间来度量的,而花费在垃圾收集之外的时间就是应用程序时间。
通过指令:-XX:GCTimeRatio=<nnn>指定,垃圾收集时间与应用程序总时间的比例为1/(1+<nnn>),例如-XX:GCTimeRatio=19表示垃圾回收时间占比为1/20,即垃圾回收时间占程序运行总时间的5%。
花费在垃圾收集上的时间是所有垃圾收集引起的暂停的总时间。如果吞吐量目标没有得到满足,那么垃圾收集器的一个可能操作是增加堆的大小,这可能导致应用程序最大暂停时间更长。
内存占用
如果吞吐量和最大暂停时间目标已经满足,垃圾收集器会减少堆的大小,直到其中一个目标(通常是吞吐量目标)不能满足为止。可以使用-Xms=<nnn>和-Xmx =<mmm>分别指定垃圾收集器可用的最小堆大小和最大堆大小。
调优策略
根据所选的吞吐量目标,堆大小会增大或收缩直到能满足目标,了解堆调优策略:例如指定最大堆大小和最大暂停时间。
不要直接指定堆大小为最大值,除非知道应用程序需要比默认值更大的堆空间:通常选择指定一个合适的吞吐量,让GC自行调整堆大小。
应用程序行为的变化可能导致堆增大或缩小。例如,程序开始以更高的速率分配内存,为了维持吞吐量不变,堆会增大。
如果堆增大到最大大小,而吞吐量目标依然没有被满足,那么最大堆大小对于吞吐量目标来说太小了。将最大堆大小设置为接近平台上总的物理内存的值,且不引起不同应用程序在内存中的切换。再次执行应用程序。如果吞吐量目标仍然没有达到,那么对于平台上的可用内存来说,应用程序时间的目标太高了。
如果吞吐量目标可以满足,但是暂停时间太长,那么选择指定最大暂停时间。选择最大暂停时间可能意味着无法满足吞吐量目标,因此选择对应用程序来说可以接受的折衷值。
当垃圾收集器试图满足相互竞争的目标时,堆的大小通常会发生波动,即使应用程序已经达到稳定状态。实现吞吐量目标(可能需要更大的堆)需要与最大暂停时间和最小内存占用(两者都可能需要较小的堆)的目标竞争。