Java性能调优指南–有关提高Java代码性能的各种技巧。

最近又学到了很多新知识,感谢优锐课老师细致地讲解,这篇博客记录下自己所学所想。

1. 介绍

在Java世界中,我们大多数人习惯于在Java应用程序开发的所有阶段使用GUI工具:编写代码,对其进行调试和分析。我们通常更喜欢在开发环境中设置服务器环境,并尝试使用熟悉的工具在本地重现问题。不幸的是,由于各种原因,通常不可能在本地重现一些问题。例如,你可能无权访问服务器应用程序处理的真实客户端数据。

在这种情况下,你需要在服务器盒上远程对应用程序进行故障排除。你应该记住,你无法使用裸露的JRE来正确地对应用程序进行故障排除:它包含所有故障排除功能,但是实际上无法访问它。结果,你需要在同一盒子上使用JDK或某些第三方工具。本文将介绍JDK工具,因为与许多组织中需要安全审核的任何第三方工具相比,你可能被允许在生产环境中使用它。

通常,仅需将JDK发行包解压缩到你的包装盒中就足够了——你不需要出于故障排除的目的而正确安装它(实际上,在很多情况下不希望正确安装)。 对于基于JMX的功能,你实际上可以安装任何Java 7/8 JDK,但是某些工具无法识别将来的JDK,因此我建议你安装最新的Java 7/8 JDK或与服务器JRE完全匹配的内部版本-它允许 你会为当前没有访问安全点的应用程序转储应用程序堆(某些处于空闲模式的应用程序是“无安全点”应用程序的简单示例)。

2. 故障排除方案

2.1. 获取正在运行的JVM的列表

为了开始工作,你几乎总是需要获取正在运行的JVM,它们的进程ID和命令行参数的列表。 有时可能就足够了:你可能会发现同一应用程序的第二个实例同时执行相同的工作(并损坏输出文件/重新打开套接字/执行其他一些愚蠢的操作)。

只需运行jcmd而无需任何参数。 它将向你显示正在运行的JVM的列表:

 3824 org.jetbrains.idea.maven.server.RemoteMavenServer
2196
780 sun.tools.jcmd.JCmd

现在,你可以通过运行jcmd <PID> help命令来查看哪些诊断命令可用于给定的JVM。 这是VisualVM的示例输出:

 >jcmd 3036 help

 3036:
The following commands are available:
JFR.stop
JFR.start
JFR.dump
JFR.check
VM.native_memory
VM.check_commercial_features
VM.unlock_commercial_features
ManagementAgent.stop
ManagementAgent.start_local
ManagementAgent.start
GC.rotate_log
Thread.print
GC.class_stats
GC.class_histogram
GC.heap_dump
GC.run_finalization
GC.run
VM.uptime
VM.flags
VM.system_properties
VM.command_line
VM.version
help

键入jcmd <PID> <COMMAND_NAME>来运行诊断命令或得到一条错误消息,询问命令参数:

 >jcmd 3036 GC.heap_dump

 3036:
java.lang.IllegalArgumentException: The argument 'filename' is mandatory.

你可以使用以下命令获取有关诊断命令参数的更多信息:jcmd <PID>帮助<COMMANDNAME>。 例如,这是GC.heap_dump命令的输出:

 >jcmd 3036 help GC.heap_dump

 3036:
GC.heap_dump
Generate a HPROF format dump of the Java heap. Impact: High: Depends on Java heap size and content. Request a full GC unless the '-all' option is specified. Permission: java.lang.management.ManagementPermission(monitor) Syntax : GC.heap_dump [options] <filename> Arguments:
filename : Name of the dump file (STRING, no default value) Options: (options must be specified using the <key> or <key>=<value> syntax)
-all : [optional] Dump all objects, including unreachable objects (BOOLEAN, false)

2.2. 进行堆转储

jcmd为你提供了一个方便的界面,用于以HPROF格式进行堆转储。只需运行jcmd <PID> GC.heap_dump <FILENAME>。请注意,文件名是相对于正在运行的JVM当前目录而不是当前目录的,因此你可能需要指定完整路径。最好使用.hprof扩展名作为转储文件名。

线程转储完成后,你可以将文件复制到自己的盒子中,然后在VisualVM(它是JDK的一部分)中打开它,并使用其堆walker和查询语言功能,或将其加载到Java Mission ControlJOverflow插件中并对其进行分析各种内存问题。

注意1:当然,还有许多其他工具可以处理hprof文件:NetBeans,Eclipse Memory Analyzer,YourKit等。将.hprof文件下载到框中后,请使用你喜欢的工具。

注意2:你也可以使用jmap工具进行堆转储:jmap -dump:live,file = <FILE_NAME> <PID>。问题在于它被正式证明为不受支持。我们中的许多人都认为JDK中不受支持的内容将永远存在,但事实证明情况不再如此:JEP 240,JEP 241

2.3. 分析类直方图

如果你正在寻找内存泄漏,通常只对堆中某些特定类型的活动对象感兴趣。 例如,你可能知道一次只能拥有一个特定类型的对象(应用程序中的某种主要工作类)。 在旧的一代中可能还存在一个或多个相同类的实例,到目前为止,这些实例尚未进行垃圾回收,但是不应从应用程序根目录访问它们。

要打印类直方图,请运行以下两个命令之一(两个命令均打印活动对象的数量):

 jcmd <PID> GC.class_histogram
jmap -histo:live <PID>

以下是示例输出的前几行:

  num     #instances         #bytes  class name
----------------------------------------------
1: 5923 5976952 [I
2: 50034 4127704 [C
3: 49465 1187160 java.lang.String
4: 188 1069496 [J
5: 3985 1067240 [Ljava.util.HashMap$Node;
6: 8756 982872 java.lang.Class
7: 2855 835792 [B
8: 23570 754240 java.util.HashMap$Node
9: 13964 671440 [Ljava.lang.Object;
10: 9642 308544 java.util.Hashtable$Entry
11: 4453 213744 java.util.HashMap

请注意,以字节为单位的已占用大小是一个较浅的大小–它不包含任何子对象。 很容易从char [](类名= [C]和String stats)中注意到这一事实–尽管实例数是相似的(尽管char []-s总是比String多,但是char []-的大小 s明显更大,如果String的大小包含基础char []的大小,则情况并非如此。

现在,你可以grep /搜索你感兴趣的类名称,并检查活动实例的数量。 如果看到的实例超出预期,请进行堆转储并在任何堆遍历器中对其进行分析(请参见上文)。

2.4. 进行线程转储

有时,你的应用程序可能会报告为“not doing anything/got stuck”。有很多种“stuck”的情况——死锁,高资源争用或仅是O(N10)算法来处理数百万用户的请求,在所有这些情况下,你应该知道你的应用程序线程正在执行什么以及锁将执行什么操作他们持有。

有两种类型的锁:基于同步关键字和Object.wait / notifyAll方法的原始锁,以及Java 5中引入的java.util.concurrent锁。它们之间的主要区别是前者绑定到你输入的堆栈框架上同步部分,并且在线程转储中始终可用。另一方面,后者(java.util.concurrent)不受堆栈框架限制——你可以使用一种方法输入锁,然后将其保留在另一种方法中。结果,一段时间以来,它们根本没有在线程转储中打印,即使现在它们仍然是一个选项。但是,你需要在线程转储中同时使用两种锁,以正确调查线程问题。

有3种打印应用程序线程转储的方法。你可以在Linux上运行kill -3 <PID>。 或者,你可以在任何平台上运行以下命令之一:

 jstack <PID>
jcmd <PID> Thread.print

2.5. 运行Java Flight Recorder

到目前为止,本文中提到的所有工具仅应用于快速调查。 为了进行更深入的分析,我建议使用内置的Java Flight Recorder。

运行JFR是一个三步过程:

  1. 你需要创建一个包含所需设置的JFR模板文件。为此,请运行jmc并转到“窗口”->“飞行记录模板管理器”菜单。配置文件准备就绪后,将其导出到文件中,然后将其发送到你正在使用的框中。
  2. JFR需要JDK商业许可证。现在,你需要在所需的JVM上解锁商业功能:
 jcmd <PID> VM.unlock_commercial_features
  1. 之后,你可以启动JFR。 这是命令行示例:
 jcmd <PID> JFR.start name=test duration=60s settings=template.jfc filename=output.jfr

此命令立即运行JFR(未设置delay属性),并使用template.jfc模板文件中的设置并将结果写入output.jfr(在两个文件中都使用绝对路径),收集JVM信息60秒钟。

录制完成后,你可以将.jfr文件复制到笔记本电脑并在jmcGUI中对其进行分析。 它包含几乎所有你需要对JVM进行故障排除的信息,除了完整堆转储,你可以单独创建并复制到你的机器中。

感谢阅读!最后奉上近期整理出来的一套完整的java架构思维导图,分享给大家对照知识点参考学习。

使用JDK工具进行Java服务器应用程序故障排除的更多相关文章

  1. [置顶] JDK工具(一)–Java编译器javac

    1.概述    javac.exe: Java编译器,将Java源代码转换成字节码. 2.用法    javac <选项> <源文件> (使用过程中发现,javac <源 ...

  2. JDK工具(一)–Java编译器javac

    1.概述    javac.exe: Java编译器,将Java源代码转换成字节码. 2.用法    javac <选项> <源文件> (使用过程中发现,javac <源 ...

  3. 三、jdk工具之jstack(Java Stack Trace)

    目录 一.jdk工具之jps(JVM Process Status Tools)命令使用 二.jdk命令之javah命令(C Header and Stub File Generator) 三.jdk ...

  4. 通过JDK常用工具监控Java进程的内存占用情况

    目录 1 JDK 工具的使用 2 查看 GC 日志信息 3 添加 JMS 远程监控 Tomcat是一款常用的Web容器, 它是运行在 JVM(Java Virtual Machine) 中的一个Jav ...

  5. 八、jdk工具之JvisualVM、JvisualVM之二--Java程序性能分析工具Java VisualVM

    目录 一.jdk工具之jps(JVM Process Status Tools)命令使用 二.jdk命令之javah命令(C Header and Stub File Generator) 三.jdk ...

  6. 十、jdk工具之Jdb命令(The Java Debugger)

    目录 一.jdk工具之jps(JVM Process Status Tools)命令使用 二.jdk命令之javah命令(C Header and Stub File Generator) 三.jdk ...

  7. Windows 7下配置JDK环境变量,JAVA环境变量配置,Tomcat服务器的使用

    参考来源: http://www.cnblogs.com/pannysp/archive/2012/03/07/2383364.html 1. 常识: 1.1 War包 War包一般是在进行Web开发 ...

  8. 使用linux perf工具生成java程序火焰图

    pre.cjk { font-family: "Nimbus Mono L", monospace } p { margin-bottom: 0.1in; line-height: ...

  9. 五、jdk工具之jmap(java memory map)、 mat之四--结合mat对内存泄露的分析、jhat之二--结合jmap生成的dump结果在浏览器上展示

    目录 一.jdk工具之jps(JVM Process Status Tools)命令使用 二.jdk命令之javah命令(C Header and Stub File Generator) 三.jdk ...

随机推荐

  1. Liunx(centos8)下的yum的基本用法和实例

    yum 命令 Yum(全称为 Yellow dog Updater, Modified)是一个在Fedora和RedHat以及CentOS中的Shell前端软件包管理器.基于RPM包管理,能够从指定的 ...

  2. Ubuntu 获取 root 用户权限并以 root权限登录

    操作步骤: 1.打开终端,使用 sudo passwd root 命令进行 Ubuntu 中密码的重置        2.切换到 /usr/share/lightdm/lightdm.conf.d 目 ...

  3. java泛型梳理

    java泛型梳理 概述 泛型,即参数化类型,是在JDK1.5之后才开始引入的. 所谓参数化类型是指所操作的数据类型在定义时被定义为一个参数,然后在使用时传入具体的类型. 这种参数类型可以用在类,接口, ...

  4. Linux 常用工具openssh之scp

    前言 scp命令用于在Linux下进行远程拷贝文件的命令,和它类似的命令有cp,不过cp只是在本机进行拷贝不能跨服务器,而且scp传输是加密的.可能会稍微影响一下速度.当你服务器硬盘变为只读read  ...

  5. day02_数据类型转换、运算符、方法入门

    day02_数据类型转换.运算符.方法入门 数据类型 基本数据类型 四类八种 整数型 (取值范围) byte(-128~127) short(-32768~32767) int(一般默认)(-21亿~ ...

  6. 2.OpenStack 网络简介(neutron)

    OpenStack 网络简介(neutron) 概述和组件 OpenStack 网络允许您创建和管理网络对象, 如网络.子网和端口, 其他 OpenStack 服务可以使用.插件可以实现, 以适应不同 ...

  7. 命令行开启WIFI

    netsh wlan set hostednetwork allow   //netsh wlan set hostednetwork mode=disallow netsh wlan set hos ...

  8. # 通过 DockerFile 打包镜像

    在介绍 Docker 具体的操作前,先简要复习下 Docker 的架构,这样可以更好地帮助我们理解 Docker 中的各个命令. 首先我们一直对 Docker 这个叫法就有些误解,Docker 其实指 ...

  9. virtualenv 指定 python 解释器的版本

    使用如下命令为 ubuntu 系统安装 virtualenv sudo apt-get install python-virtualenv 当我们使用 virtualenv 命令创建虚拟环境时,默认使 ...

  10. lua学习之表达式篇

    表达式 表达式用于表达值 lua 中表达式可以为数字常量,自变字符串,变量,一元和二元操作符,函数调用.函数定义.table 构造式 算数操作符 一元操作符 -负号 二元操作符 -减号 / ^ % x ...