Jvm参数

2016/01/04 JVM

JVM参数

在虚拟机的运行过程中,如果可以跟踪系统的运行状态,那么对于问题的故障排查会有一定的帮助。因此虚拟机提供了一些跟踪系统状态的参数,使用给定的参数执行Java虚拟机,就可以在系统运行时打印日志,用于问题分析。

跟踪垃圾回收器

Java的一大特色就是支持自动的垃圾回收(GC),但是有时候,如果垃圾回收频繁出现,或者占用了太长的CPU时间,就不得不引起重视。此时,就需要一些跟踪参数来进一步甄别垃圾回收器的效率和效果。

-XX:+PrintGC

-XX:+PrintGC –简单打印GC日志,示例代码如下


public class GcParameterTest {

    public static void main(String[] args) {
        byte[] bytes = new byte[1024*1024*6];
        bytes=null;
        System.gc();
    }
}

使用这个参数启动Java虚拟机后,只要遇到GC,就会打印日志

[GC (System.gc())  11233K->896K(241664K), 0.0011581 secs]
[Full GC (System.gc())  896K->632K(241664K), 0.0044786 secs]

GC 和 Full GC指垃圾收集的停顿类型,而不是指新生代GC和老年代GC; GC前java堆已使用容量 -> GC后java堆已使用容量(Java堆总容量)

该日志显示,一共进行了一次GC和一个FullGC,每次GC占用一行,在GC前,堆空间使用量约为11233K,两次GC后,堆空间使用量为632K,当前可用的堆空间总和约为241664K。最后,显示的是本次GC所花费的时间。

-XX:+PrintGCDetails

-XX:+PrintGCDetails –详细打印GC日志 使用这个参数启动Java虚拟机后,只要遇到GC,就会打印日志

[GC (System.gc()) [PSYoungGen: 11233K->840K(73728K)] 11233K->848K(241664K), 0.0016969 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 840K->0K(73728K)] [ParOldGen: 8K->632K(167936K)] 848K->632K(241664K), [Metaspace: 3016K->3016K(1056768K)], 0.0049945 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
Heap
 PSYoungGen      total 73728K, used 635K [0x000000076e000000, 0x0000000773200000, 0x00000007c0000000)
  eden space 63488K, 1% used [0x000000076e000000,0x000000076e09ecf8,0x0000000771e00000)
  from space 10240K, 0% used [0x0000000771e00000,0x0000000771e00000,0x0000000772800000)
  to   space 10240K, 0% used [0x0000000772800000,0x0000000772800000,0x0000000773200000)
 ParOldGen       total 167936K, used 632K [0x00000006ca000000, 0x00000006d4400000, 0x000000076e000000)
  object space 167936K, 0% used [0x00000006ca000000,0x00000006ca09e238,0x00000006d4400000)
 Metaspace       used 3023K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 321K, capacity 386K, committed 512K, reserved 1048576K
Disconnected from the target VM, address: '127.0.0.1:62297', transport: 'socket'

Process finished with exit code 0

从这个输出中可以看到,系统经历了2次GC,第1次仅为新生代GC,回收的效果是新生代从回收前的11233K左右降低到840K。第2次为FullGC,它同时回收了新生代、老年代和元数据。日志显示,新生代在这次GC中全部释放空间,老年代从848K降低到了632K。元数据区的大没有变化。在日志的最后,显示了GC所花费的时间,其中usr表示用户态CPU耗时,sys表示系统CPU耗时,real表示GC实际经历的时间。

还会使虚拟机在退出前打印堆的详细信息,详细信息描述了当前堆的各个区间的使用情况。如上输出所示,当前新生代(PSYoungGen)总大小为73728K,已使用635K。紧跟其后的3个16进制数字表示新生代的下界、当前上界和上界。使用上界减去下界就能得到当前堆空间的最大值,使用当前上界减去下界,就是当前虚拟机已经为程序分配的空间大小。如果当前上界等于下界,说明当前的堆空间已经没有扩大的可能。

-XX:+PrintHeapAtGC

-XX:+PrintHeapAtGC –打印全面的Java堆信息

{Heap before GC invocations=1 (full 0):
 PSYoungGen      total 73728K, used 11233K [0x000000076e000000, 0x0000000773200000, 0x00000007c0000000)
  eden space 63488K, 17% used [0x000000076e000000,0x000000076eaf8740,0x0000000771e00000)
  from space 10240K, 0% used [0x0000000772800000,0x0000000772800000,0x0000000773200000)
  to   space 10240K, 0% used [0x0000000771e00000,0x0000000771e00000,0x0000000772800000)
 ParOldGen       total 167936K, used 0K [0x00000006ca000000, 0x00000006d4400000, 0x000000076e000000)
  object space 167936K, 0% used [0x00000006ca000000,0x00000006ca000000,0x00000006d4400000)
 Metaspace       used 3016K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 320K, capacity 386K, committed 512K, reserved 1048576K
Heap after GC invocations=1 (full 0):
 PSYoungGen      total 73728K, used 920K [0x000000076e000000, 0x0000000773200000, 0x00000007c0000000)
  eden space 63488K, 0% used [0x000000076e000000,0x000000076e000000,0x0000000771e00000)
  from space 10240K, 8% used [0x0000000771e00000,0x0000000771ee6010,0x0000000772800000)
  to   space 10240K, 0% used [0x0000000772800000,0x0000000772800000,0x0000000773200000)
 ParOldGen       total 167936K, used 8K [0x00000006ca000000, 0x00000006d4400000, 0x000000076e000000)
  object space 167936K, 0% used [0x00000006ca000000,0x00000006ca002000,0x00000006d4400000)
 Metaspace       used 3016K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 320K, capacity 386K, committed 512K, reserved 1048576K
}

可以看到在GC日志输出前后,都有详细的堆信息输出,分别表示GC回收前和GC回收后的堆信息,使用这个参数,可以很好地观察GC对堆空间的影响。

-XX:+PrintGCTimeStamps

该参数会在每次GC发生时,额外输出GC发生的时间,该输出时间为虚拟机启动后的时间偏移量

0.134: [GC (System.gc())  11233K->896K(241664K), 0.0020589 secs]
0.136: [Full GC (System.gc())  896K->632K(241664K), 0.0055606 secs]

-XX:+PrintGCApplicationConcurrentTime

使用参数可以打印应用程序的执行时间

Application time: 0.0429624 seconds
[GC (System.gc())  11233K->896K(241664K), 0.0010938 secs]
[Full GC (System.gc())  896K->632K(241664K), 0.0046454 secs]
Application time: 0.0009914 seconds

-XX:+PrintGCApplicationStoppedTime

使用参数可以打印应用程序由于GC而产生的停顿时间

Total time for which application threads were stopped: 0.0000418 seconds, Stopping threads took: 0.0000133 seconds
[GC (System.gc())  11233K->832K(241664K), 0.0010838 secs]
[Full GC (System.gc())  832K->632K(241664K), 0.0053828 secs]
Total time for which application threads were stopped: 0.0066878 seconds, Stopping threads took: 0.0000248 seconds

-XX:+PrintReferenceGC

使用该参数可以跟踪系统内的软引用,弱引用,虚引用和Finallize队列

-Xloggc:log/gc.log

默认情况下,GC的日志会在控制台中输出,这不便于后续分析和定位问题。为此,使用该参数-Xloggc:log/gc.log启动虚拟机,可以在当前目录下的log文件夹下的gc.log文件中记录所有的GC日志。

类的加载/卸载的跟踪

Java程序的运行离不开类的加载,为了更好地理解程序的执行,有时候需要知道系统加载了哪些类。一般情况下,系统加载的类存在于文件系统中,以jar的形式打包或者以class文件的形式存在,可以直接通过文件系统查看。但是随着动态代理、AOP等技术的普遍使用,系统也极有可能在运行时动态生成某些类,这些类相对比较隐蔽,无法通过文件系统找到,为此,虚拟机提供的类加载/卸载跟踪参数就显得格外有意义。

-verbose:class

使用该参数可以跟踪类加载和卸载

-XX:+TraceClassLoading

使用该参数可以跟踪类的加载

-XX:+TraceClassUnloading

使用该参数可以跟踪类的卸载

-XX:+PrintClassHistogram

使用该参数可以在运行时打印、查看系统中类的分布情况,只要在系统启动时加上该参数参数,然后在Java的控制台中按下Ctrl+Break组合键,控制台上就会显示当前的类信息柱状图。

系统参数查看

由于目前的Java虚拟机支持众多的可配参数,不同的参数可能对系统的执行效果有较大的影响,因此,有必要明确当前系统的实际运行参数。虚拟机提供了一些手段来帮助研发人员获得这些参数。

-XX:+PrintVMOptions

-XX:+PrintVMOptions 使用该参数可以在程序运行时,打印虚拟机接受到的命令行显式参数。

-XX:+PrintCommandLineFlags

-XX:+PrintCommandLineFlags 参数可以打印传递给虚拟机的显式和隐式参数,隐式参数未必是通过命令行直接给出的,它可能是由虚拟机启动时自行设置的

-XX:+PrintFlagsFinal

-XX:+PrintFlagsFinal 如果需要查看系统的详细参数,开启这个参数后,虚拟机可能会产生多达500多行的输出,每一行为一个配置参数和其当前取值

堆的配置

堆空间是Java进程的重要组成部分,几乎所有与应用相关的内存空间都和堆有关。下面将主要介绍与堆有关的参数设置,这些参数可以说是Java虚拟机中最重要的,也是对程序性能有着重要影响的。

最大堆和初始堆的设置

当Java进程启动时,虚拟机就会分配一块初始堆空间,可以使用参数Xms 指定这块空间的大小。一般来说,虚拟机会尽可能维持在初始堆空间的范围内运行。但是如果初始堆空间耗尽,虚拟机将会对堆空间进行扩展,其扩展上限为最大堆空间,最大堆空间可以使用数 -Xmx 指定。

根据上面介绍很容易让人想到,这里的最大可用内存就是指-Xmx的取值,当前总内存应该不小于-Xms的设定,因为当前总内存总是在-Xms和-Xmx之间,从-Xms开始根据需要向上增长。而当前空闲内存应该是当前总内存减去当前已经使用的空间。这样的理解差不多是正确的,之所以使用“差不多”一词,是因为这样理解并不是十分全面的。

实际上这是分配给堆的内存空间和实际可用的内存空间并非一个概念。由于垃圾回收的需要,虚拟机会对堆空间进行分区管理,不同的区域采用不同的回收算法,一些算法会使用空间换时间的策略工作,因此会存在可用内存的损失,最终的结果就是实际可用内存会浪费大小等于from/to的空间。因此,实际最大可用内存为-Xmx的值减去from的大小。实际上发现这个计算也有偏差,这个偏差是由于虚拟机内部并没有直接使用新生代from/to的大小,而是进一步对它们做了对齐操作。对于串行GC的情况,虚拟机使用某种方法估算from/to的大小,并进行了对齐。

在实际工作中,也可以直接将初始堆-Xms与最大堆-Xmx设置相等。这样的好处是可以减少程序运行时进行的垃圾回收次数,从而提高程序的性能。

-Xms -Xmx

-Xms初始堆大小 -Xmx最大堆大小

新生代的配置

参数 -Xmn 可以用于设置新生代的大小。设置一个较大的新生代会减小老年代的大小,这个参数对系统性能以及GC行为有很大的影响。新生代的大小一般设置为整个堆空间的13到1/4左右。 参数 -XX:SurvivorRatio 用来设置新生代中eden空间和from/to空间的比例关系,它的含义如下:-XX:SurvivorRatio=eden/from=eden/to

-XX:SurvivorRatio可以设置eden区与survivor区的比例.-XX:NewRatio可以设置老年代与新生代的比例。

堆溢出处理

-XX:+HeapDumpOnOutOfMemoryError

-XX:+HeapDumpOnOutOfMemoryError 使用该参数,可以在内存溢出时导出整个堆信息

-XX:HeapDumpPath

-XX:HeapDumpPath 使用该参数,可以指定导出堆的存放路径。

非堆内存的参数设置

除了堆内存外,虚拟机还有一些内存用于方法区、线程栈和直接内存的使用。它们与堆内存是相对独立的。虽然和堆内存相比,这些内存空间和应用程序本身可能关系不那么密切,但是从系统层面上看,有效、合理地配置这些内存参数,对系统性能和稳定性也有着重要的作用。

方法区配置

方法区主要存放类的元信息。 在JDK1.6和JDK1.7等版本中,可以使用-XX:PermSize和-XX:MaxPermSize配置永久区大小。其中-XX:PermSize表示初始的永久区大小,-XX:MaxPermSize表示最大永久区。 在JDK1.8中,永久区被彻底移除,使用了新的元数据区存放类的元数据。默认情况下,元数据区只受系统可用内存的限制,但依然可以使用参数-XX:MaxMetaspaceSize指定永久区的最大可用值。

栈配置

栈是每个线程私有的内存空间。在Java虚拟机中可以使用-Xss参数指定线程的栈大小。

直接内存配置

直接内存也是Java程序中非常重要的组成部分,特别是在NIO被广泛使用后,直接内存的使用也变得非常普遍。直接内存跳过了Java堆,使Java程序可以直接访问原生堆空间,因此,从一定程度上加快了内存空间的访问速度。但是,武断地认为使用直接内存一定可以提高内存访问速度也是不正确的。

最大可用直接内存可以使用参数-X:MaxDirectMemorySize设置,如不设置,默认值为最大堆空间,即-Xmx。当直接内存使用量达到-XX:MaxDirectMemorySize时,就会触发垃圾回收,如果垃圾回收不能有效释放足够空间,直接内存溢出依然会引起系统的OOM。

虽然在访问读写上直接内存有较大的优势,但是在内存空间申请时,直接内存毫无优势可言。

由此,可以得出结论:直接内存适合申请次数较少、访问较频繁的场合。如果内存空间本身需要频繁申请,则并不适合使用直接内存。

虚拟机的工作模式

目前的Java虚拟机支持Client和Server两种运行模式,使用参数-client可以指定使用Client模式,使用参数-server可以指定使用Server模式。默认情况下,虚拟机会根据当前计算机系统环境自动选择运行模式。 使用-version参数可以查看当前的模式 与Client模式相比,Server模式的启动比较慢,因为Server模式会尝试收集更多的系统性能信息,使用更复杂的优化算法对程序进行优化。因此,当系统完全启动并进入运行稳定期后,Server模式的执行速度会远远快于Client模式。所以,对于后台长期运行的系统,使用-server参数启动对系统的整体性能可以有不小的帮助。但对于用户界面程序,运行时间不长,又追求启动速度,Client模式也是不错的选择。 从发展趋势上看,未来64位系统必然会逐步取代32位系统,而在64位系统中虚拟机更倾向于使用Server模式运行。 虚拟机在Server模式和Client模式下的各种参数可能会有很大不同,可以使用-XX:+PrintFlagsFinal参数查看对应模式下的参数和其职。

GC日志

每一种回收器的日志格式都是由其自身的实现决定的,换而言之,每种回收器的日志格式都可以不一样。但虚拟机设计者为了方便用户阅读,将各个回收器的日志都维持一定的共性。JavaGC日志中简单介绍了这些共性。 各分区的大小对GC的性能影响很大。如何将各分区调整到合适的大小,分析活跃数据的大小是很好的切入点。 活跃数据的大小是指,应用程序稳定运行时长期存活对象在堆中占用的空间大小,也就是Full GC后堆中老年代占用空间的大小。可以通过GC日志中Full GC之后老年代数据大小得出,比较准确的方法是在程序稳定后,多次获取GC数据,通过取平均值的方式计算活跃数据的大小。

Search

    微信好友

    博士的沙漏

    Table of Contents