深入JVM-常用Java虚拟机参数

时间:2022-12-27 14:20:43

一、跟踪调试参数

1.1 跟踪垃圾回收-读懂虚拟机日志

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

最简单的一个GC参数是-XX:+PrintGC,使用这个参数启动Java虚拟机后,只要遇到GC,就会打印日志。

如果需要更加详细的信息,则可以使用-XX:+PrintGCDetails参数。他的输出可能如下:

深入JVM-常用Java虚拟机参数

从这个输出可以看到,系统经历了3次GC,第一次仅为新生代GC,回收的效果是新生代从回收前的8MB左右降到了1M左右。整个堆从22MB左右降到了17MB。

第二次为Full GC,它同时回收了新生代、老年代和永久区。从日志显示,新生代在这次GC中没有释放空间(严格来说,这是GC日志的一个小bug,事实上,在这次Full GC完成后,新生代被清空,由于GC日志输出时机的关系,各个版本的JDK日志多少有些不太精确的地方),老年代从16MB降到了13MB。整个堆大小从26MB左右降到了13MB左右(这个大小完全与老年代实际大小相等,因此可以推断,新生代实际上已被清空)。永久区的大小没有变化。日志的最后,显示了GC花费的时间,其中user表示用户态CPU耗时,sys表示系统CPU耗时,real表示GC实际经历的时间。

如果需要更全面的堆信息,还可以使用参数-XX:+PrintHeapAtGC。他会在每次GC前后分别打印堆的信息。

如果需要分析GC发生的时间,还可以使用-XX:+PrintGCTimeStamps参数,该参数会在每次GC发生时,额外输出GC发生的时间,该输出时机为虚拟机启动后的时间偏移量。

默认情况下,GC的日志会在控制台输出,这不便于后续分析和定位问题。为此,虚拟机允许将GC日志以文件的形式输出,可以使用参数-Xloggc指定。

1.2 类加载/卸载的跟踪

一般情况下,系统加载的类存在于文件系统中,以jar的形式打包或者以class文件的形式存在,可以直接通过文件系统查看。但是随着动态代理、AOP等技术的普遍使用,系统也ji'ke'neng极可能在运行时动态生成某些类,这些类相对比较隐蔽,无法通过文件系统找到。

可以使用参数-verbose:class跟踪类的加载和卸载,也可以单独使用参数-XX:+TraceClassLoading跟踪类的加载,使用参数-XX:+TraceClassUnloading跟踪类的卸载。

Java虚拟机还允许研发人员在运行时打印、查看系统中类的分布情况,只要在系统启动时加上-XX:+PrintClassHistogram参数,然后在Java控制台按下Ctrl+Break组合键,控制台就会显示当前的类信息柱状图。

1.3 系统参数查看

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

参数-XX:+PrintCommandLineFlags可以打印传递给虚拟机的显式和隐式参数。

二、学习堆的配置参数

2.1 最大堆和初始堆的设置

  • Xms:初始堆
  • Xmx:最大堆

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

2.2 新生代的配置

参数-Xmn可以用于设置新生代的大小。设置一个较大的新生代会减小老年代的大小,这个参数对系统性能以及GC行为有很大的影响,新生代的大小一般设置为整个堆空间的1/3到1/4左右

参数-XX:SurvivorRatio用来设置新生代中eden空间和from/to空间的比例关系,他的含义如下:

-XX:SurvivorRatio=eden/from=eden/to

实际工作中,堆分布的基本策略是:尽可能将对象预留在新生代,减少老年代GC的次数。

除了可以使用-Xmn指定新生代的绝对大小外,还可以使用参数-XX:NewRatio来设置新生代和老年大的比例,他的含义如下:

-XX:NewRatio=老年代/新生代

深入JVM-常用Java虚拟机参数

2.3 堆溢出处理

在Java程序的运行过程中,如果堆空间不足,则有可能抛出内存溢出错误(OutOfMemory),简称OOM。一旦发生这类问题,系统就会*退出。如果发生在生产环节,可能会引起严重的业务中断。为了不断改善系统,避免或减少这类错误的发生,需要在发生错误时,获得尽可能多的现场信息。Java虚拟机提供了参数-XX:+HeapDumpOnOutOfMemoryError,使用该参数,可以再内存溢出时导出整个堆信息。和他配合使用的还有-XX:HeapDumpPath,可以指定导出堆的存放路径。

三、非堆内存的参数配置

3.1 方法区配置

方法区主要存放类的元信息。

在JDK1.6和JDK 1.7等版本中,可以使用-XX:PermSize和-XX:MaxPermSize配置永久区大小。其中-XX:PermSize表示初始的永久区大小,-XX:MaxPermSize表示最大永久区。

在JDK 1.8中,永久区被彻底移除,使用了新的元数据区存放类的元数据。默认情况下,元数据区只受系统可用内存的限制,但依然可以使用参数-XX:MaxMetaspaceSize指定元数据区的最大可用值。

3.2 栈配置

通过-Xss指定线程的栈大小。

3.3 直接内存配置

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

四、虚拟机的工作模式

目前Java虚拟机支持Client和Server两种运行模式。默认情况下,虚拟机会根据当前计算机系统环境自动选择运行模式。使用-version参数可以查看当前的模式,如下:

java version "1.8.0_101"
Java(TM) SE Runtime Environment (build 1.8.0_101-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.101-b13, mixed mode)

与Client模式相比,Server模式启动较慢,因为Server模式会尝试收集更多的系统性能信息,使用更复杂的优化算法对程序进行优化。因此,当系统完全启动并进入运行稳定期后,Server模式的执行速度会远远快于Client模式。