JVM 是多数开发人员视为理所当然的 Java 功能和性能背后的重负荷机器然而我们很少有人能理解 JVM 是如何进行工作的 — 像任务分配和垃圾收集转动线程打开和关闭文件中断和/或 JIT 编译 Java 字节码等等不熟悉 JVM 将不仅会影响应用程序性能而且当 JVM 出问题时尝试修复也会很困难本文将介绍一些命令行标志您可以使用它们来诊断和调优您的 Java 虚拟机性能 DisableExplicitGC我已记不清有多少次用户要求我就应用程序性能问题提供咨询了其实只要跨代码快速运行 grep就会发现清单 所示的问题 — 原始 java 性能反模式 清单 Systemgc(); 显式垃圾收集是一个非常糟糕的主意 — 就像将您和一个疯狂的斗牛犬锁在一个电话亭里尽管调用的语法是依赖实现的但如果您的 JVM 正在运行一个分代的垃圾回收器(大多数是)Systemgc(); 强迫 VM 执行一个堆的 全部清扫虽然有的没有必要全部清扫比一个常规 GC 操作要昂贵好几个数量级这只是个简单数学问题 您可以不把我的话放在心上 — Sun 的工程师为这个特殊的人工错误提供一个 JVM 标志; XX:+DisableExplicitGC 标志自动将 Systemgc() 调用转换成一个空操作为您提供运行代码的机会您自己看看 Systemgc() 对于整个 JVM 执行有害还是有利 HeapDumpOnOutOfMemoryError您有没有经历过这样的情况JVM 不能使用不断抛出 OutOfMemoryError而您又不能为自己创建调试器来捕获它或查看出现了什么问题?像这类偶发和/或不确定的问题通常使开发人员发疯 买者自负并不是任何 VM 都支持所有命令行标志Sun/Oracle 的 VM 除外查明一个标志是否被支持的最好方法是试用它看它是否正常工作倘若这些标志在技术上是不支持的那么使用它们您要承担全部责任如果这些标志中的任何一个使您的代码您的数据您的服务器或您的一切消失得无影无蹤我Sun/Oracle 和 IBM? 都将不负责任为以防万一建议先在虚拟(非常生产)环境中实验 在这个时刻您想要的是在 JVM 消亡之际捕获堆的一个快照 — 正好 XX:+HeapDumpOnOutOfMemoryError 命令可以完成这一操作 运行该命令通知 JVM 拍摄一个 堆转储快照并将其保存在一个文件中以便处理通常使用 jhat 实用工具您可以使用相应的 XX:HeapDumpPath 标志指定到保存文件的实际路径(不管文件保存在哪务必确保文件系统和/或 Java 流程必须要有权限配置可以在其中写入) bootclasspath定期将一个类放入类路径是很有帮助的这类路径与库存 JRE 附带的类路径或者以某种方式扩展的 JRE 类路径略有不同(新 Java Crypto API 提供商就是一个例子)如果您想要扩展 JRE 那么您定制的实现必须可以使用引导程序 ClassLoader该引导程序可以加载 rtjar 中的 javalangObject 及其所有相关文件 尽管您可以 非法打开 rtjar 并将您的定制实现或新数据包移入其中但从技术上您就违反了您下载 JDK 时同意的协议了 相反使用 JVM 自己的 Xbootclasspath 选项以及皮肤 Xbootclasspath/p 和 Xbootclasspath/a Xbootclasspath 使您可以设置完整的引导类路径(这通常包括一个对 rtjar 的引用)以及一些其他 JDK 附带的(不是 rtjar 的一部分)JAR 文件Xbootclasspath/p 将值前置到现有 bootclasspath 中并将 Xbootclasspath/a 附加到其中 例如如果您修改了库中的 javalangInteger并将修改放在一个子路径 mods 下那么 Xbootclasspath/a mods 参数将新 Integer 放在默认的参数前面 verbose对于虚拟的或任何类型的 Java 应用程序verbose 是一个很有用的一级诊断使用程序该标志有三个子标志gcclass 和 jni 开发人员尝试寻找是否 JVM 垃圾收集器发生故障或者导致性能低下通常首先要做的就是执行 gc不幸的是解释 gc 输出很麻烦 — 足够写一本书更糟糕的是在命令行中打印的输出在不同的 Java 版本中或者不在不同的 JVM 中会发生改变这使得正确解释变得更难 一般来说如果垃圾收集器是一个分代收集器(多数 企业级 VMs 都是)某种虚拟标志将会出现来指出一个全部清扫 GC 通路;在 Sun JVM 中标志在 GC 输出行的开始以 [Full GC ] 形式出现 想要诊断 ClassLoader 和/或不匹配的类沖突class 可以帮上大忙它不仅报告类何时加载还报告类从何处加载包括到 JAR 的路径(如果来自 JAR) jni 很少使用除了使用 JNI 或本地库时打开时它将报告各种 JNI 事件比如本地库何时加载方法何时弹回;再一次强调在不同 JVM 版本中输出会发生变化 Commandline X我列出了 JVM 中提供的我喜欢的命令行选项但是还有一些更多的需要您自己发现运行命令行参数 X列出 JVM 提供的所有非标准(但大部分都是安全的)参数 — 例如 Xint在解释模式下运行 JVM(对于测试 JIT 编译器实际上是否对您的代码起作用或者验证是否 JIT 编译器中有一个 bug这都很有用) Xloggc:和 verbose:gc 做同样的事但是记录一个文件而不输出到命令行窗口 JVM 命令行选项时常发生变化因此定期查看是一个好主意甚至您深夜盯着监控器和下午 点回家和妻子孩子吃顿晚饭(或者在 Mass Effect 中消灭您的敌人根据您的喜好)它们都是不一样的 结束语 在生产环境中命令行标志不是为永久使用而设计的 — 事实上除了您终止用来调优 JVM 垃圾收集器的标志没有一个非标准命令行标记是专用于生产使用的但是作为工具来刺探在其他方面完全不透明的虚拟机的内部工作是非常有用的 |