JVM 性能调优实战之 一次系统性能瓶颈的寻找过程

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

                玩过性能优化的朋友都清楚,性能优化的关键并不在于怎么进行优化,而在于怎么找到当前系统的性能瓶颈。
性能优化分为好几个层次,比如系统层次、算法层次、代码层次...JVM 的性能优化被认为是底层优化,门槛较高,精通这种技能的人比较少。笔者呆过几家技术力量不算弱的公司,每个公司内部真正能够进行 JVM 性能调优的人寥寥无几、甚至没有。如是乎,能够有效通过 JVM 调优提升系统性能的人往往被人们冠以"大牛"、"大师"之类的称呼。
其实 JVM 本身给我们提供了很多强大而有效的监控进程、分析定位瓶颈的工具,比如 JConsole、JMap、JStack、JStat 等等。使用这些命令,再结合 Linux 自身提供的一些强大的进程、线程命令,能够快速定位系统瓶颈。本文以一次使用这些工具准确定位到某系统瓶颈的实战经验为例,希望能为大家掌握 JVM 调优这项技能起到一些借鉴作用。
本文背景:
  • Linux:RedHat 6.1
  • Weblogic:11g
  • JRokit:R28.2.4
  • JDK:1.6.0_45
Weblogic 跑在 JRokit 上面,JDK 是我们对 JRokit 进行监控分析的工具。

1. LoadRunner 压测结果

该系统其实早已跑在生产上好多年,虽然没有做过压力测试,但稳定性还是可以的。公司进行架构调整,有一些新系统将对接该系统。公司领导担心对接后该系统的并发性能问题,于是开始对该系统进行并发压力测试。
50 个用户并发压十几个小时,TRT 在 20 左右,TPS 在 2.5 左右。领导对这个结果不满意,要求进行优化。但这是个老系统,开发在之前明显已经对其代码做过很多优化,如今面对这种状况也束手无策。

2. Oracle 的 awr 报告分析

有句老话,优化来优化去,系统的性能瓶颈还是会在数据库上面。在这里我们首先想到的也是数据库的问题。
并发压测的时候 Spotlight 查看数据库服务器各项性能指标,很清闲。

分析 awr 报告,结果显示也是很清闲。

并发压测的时候去相关数据表执行一些 sql 的速度很快也证明着问题不在 Oracle 这边。

3. Weblogic 服务器的性能指标

使用 Spotlight 查看并发压测时的 Weblogic 所在的 Linux 服务器,除了 CPU 之外其它各项指标显示,Linux 也很清闲。
虽然 CPU 利用率始终在 200% 左右徘徊,但这对于 16 核的系统来讲也算是正常的吧?

4. JStack 报告分析

事情到了这里,大家已经想到了线程死锁、等待的问题了。
没错,JStack 隆重登场。JStack 能够看到当前 Java 进程中每个线程的当前状态、调用栈、锁住或等待去锁定的资源,而且很强悍的是它还能直接报告是否有线程死锁,可谓解决线程问题的不二之选。
$ /opt/jdk1.6.0_45/bin/jstack -l 10495 > ~/10495jstack.txt
JRokit 堆栈的拉取,可以直接用 JDK 的 JStack,10495 是 Weblogic 服务的进程 ID。注意一定要用该进程的启动用户去拉,否则会报 Unable to open socket file: target process not responding or HotSpot VM not loaded 错误。
JStack 拉取的文件信息基本分为以下几个部分:
  • 该拉取快照的服务器时间
  • JVM 版本
  • 以线程 ID(即 tid)升序依次列出当前进程中每个线程的调用栈
  • 死锁(如果有的话)
  • 阻塞锁链
  • 打开的锁链
  • 监视器解锁情况跟踪
每个线程在等待什么资源,这个资源目前在被哪个线程 hold,尽在眼前。JStack 最好在压测时多次获取,找到的普遍存在的现象即为线程瓶颈所在。

4.1. TLA 空间的调整

多次拉取 JStack,发现很多线程处于这个状态:
    at jrockit/vm/Allocator.getNewTla(JJ)V(Native Method)
    at jrockit/vm/Allocator.allocObjectOrArray(Allocator.java:354)[optimized]
    at java/util/HashMap.resize(HashMap.java:564)[inlined]
    at java/util/LinkedHashMap.addEntry(LinkedHashMap.java:414)[optimized]
    at java/util/HashMap.put(HashMap.java:477)[optimized]
由此怀疑出现上述堆栈的原因可能是 TLA 空间不足引起。TLA 是 thread local area 的缩写,是每个线程私有的空间,所以在多线程环境下 TLA 带来的性能提升是显而易见的。如果大部分线程的需要分配的对象都较大,可以考虑提高 TLA 空间,因为这样更大的对象可以在 TLA 中进行分配,这样就不用担心和其它线程的同步问题了。但这个也不可以调的太大,否则也会带来一些问题,比如会带来更多内存碎片、更加频繁的垃圾搜集。
TLA 默认最小大小 2 KB,默认首选大小 16 KB - 256 KB (取决于新生代分区大小)。这里我们调整 TLA 空间大小为最小 32 KB,首选 1024 KB,JVM 启动参数中加入:
-XXtlaSize:min=32k,preferred=1024k

5. JStat 结合 GC 日志报告分析

第 4 步参数生效之后继续压测,TLA 频繁申请是降下来了,但 TRT 仍旧是 20,TPS 依旧 2.5。别灰心,改一个地方就立竿见影,胜利似乎来得太快了点。
现在怀疑是服务堆内存太小,查看一下果然。服务器物理内存 32 GB,Weblogic 进程只分到了 6 GB。怎么查看?至少有四种办法:

5.1. ps 命令

$ ps -ef | grep java
defonds     29874 29819  2 Sep03 ?        09:03:17 /opt/jrockit-jdk1.6.0_33/bin/jav a -jrockit -Xms6000m -Xmx6000m -Dweblogic.Name=AdminServer -Djava.security.policy=

5.2. Weblogic 控制台

登录 Weblogic 管理控制台 -> 环境 -> 服务器,选择该服务器实例 -> 监视 -> 性能 -> 当前堆大小。
这个页面还能看到进程已运行时间,启动以来发生的 GC 次数,可以折算出 GC 的频率,为本次性能瓶颈 - GC 过于频繁提供有力的佐证。

5.3. GC 日志报告

开启 JRokit GC 日志报告只需在 Java 进程启动参数里加入
-Xverbose:memory -Xverboselog:verboseText.txt
GC 日志将会被输出到 verboseText.txt 文件,这个文件一般会生成在启动的 Weblogic 域目录下。如果找不着也可以用 find 命令去搜:
$ find /appserver/ -name verboseText.txt
/appserver/Oracle/Middleware/user_projects/domains/defonds_domain/verboseText.txt
GC log 拿到后,第 3 行中的 maximal heap size 即为该进程分配到的最大堆大小:
[INFO ][memory ] Heap size: 10485760KB, maximal heap size: 10485760KB, nursery size: 5242880KB.
下面还有进程启动以后较为详细的每次 GC 的信息:
[INFO ][memory ] [YC#2547] 340.828-340.845: YC 10444109KB->10417908KB (10485760KB), 0.018 s, sum of pauses 17.294 ms, longest pause 17.294 ms.
[INFO ][memory ] [YC#2548] 340.852-340.871: YC 10450332KB->10434521KB (10485760KB), 0.019 s, sum of pauses 18.779 ms, longest pause 18.779 ms.
[INFO ][memory ] [YC#2549] 340.878-340.895: YC 10476739KB->10485760KB (10485760KB), 0.017 s, sum of pauses 16.520 ms, longest pause 16.520 ms.
[INFO ][memory ] [OC#614] 340.895-341.126: OC 10485760KB->10413562KB (10485760KB), 0.231 s, sum of pauses 206.458 ms, longest pause 206.458 ms.
第一行表示该进程启动后的第 340.828 秒 - 340.845 秒期间进行了一次 young gc,该次 GC 持续了 17.294 ms,将整个已用掉的堆内存由 10444109 KB 降低到 10417908 KB。
第三行同样是一次 young gc,但该次 GC 后已用堆内存反而上升到了 10485760 KB,也就是达到最大堆内存,于是该次 young gc 结束的同时触发 full gc。
第四行是一次 old gc (即 full gc),将已用堆内存由 10485760 KB 降到了 10413562 KB,耗时 206.458 ms。
这些日志同样能够指出当前压力下的 GC 的频率,为本次性能瓶颈 - GC 过于频繁提供有力的佐证。

5.4. JStat 报告

跟 JStack 的拉取一样,可以直接用 JDK 的 JStat 去拉取 JRokit 的 GC 信息:
$ /opt/jdk1.6.0_45/bin/jstat -J-Djstat.showUnsupported=true -snap 10495 > ~/10495jstat.txt
注意这个信息是一个快照,这是跟 GC 日志报告不同的地方。
jrockit.gc.latest.heapSize=10737418240
jrockit.gc.latest.nurserySize=23100384
上述是当前已用碓大小和新生代分区大小。多拉几次即可估算出各自分配的大小。

5.5. 内存分配

根据 5.1 - 5.4 我们得出当前服务器分配堆内存太小的结论,根据 5.3 GC 日志报告和 5.4. JStat 报告可以得出新生代分区太小的结论。
于是我们调整它们的大小,结合 4.1 TLA 调整的结论,JVM 启动参数增加以下:
-Xms10240m -Xmx10240m -Xns:1024m -XXtlaSize:min=32k,preferred=1024k
再次压测,TRT 降到了 2.5,TPS 上升到 20。

6. 性能瓶颈的定位

很明显,上述 JVM 调整没有从根本上解决性能问题,我们还没有真正定位到系统性能瓶颈。

6.1. 性能线程的定位

6.1.1. 性能进程的获取

使用 TOP 命令拿到最耗 CPU 的那个进程:
性能进程号的获取.png
进程 ID 为 10495 的那个进程一直在占用很高的 CPU。

6.1.2. 性能线程的获取

现在我们来找到这个进程中占用 CPU 较高的那些线程:
$ ps p 10495 -L -o pcpu,pid,tid,time,tname,cmd > ~/10495ps.txt
多次拉这个快照,我们找到了 tid 为 10499、 10500、 10501、 10502 等线程一直在占用着很高的 CPU:
tid为10499、10500、10501、10502等线程占用CPU很高.png
拉 JStack 快照看看都是一些什么线程:
$ /opt/jdk1.6.0_45/bin/jstack -l 10495 > ~/10495jstack.txt
相关部分结果如下:
"(GC Worker Thread 1)" id=? idx=0x10 tid= 10499 prio=5 alive, daemon
    at pthread_cond_wait@@GLIBC_2.3.2+202(:0)@0x3708c0b44a
    at eventTimedWaitNoTransitionImpl+71(event.c:90)@0x7fac47be8528
    at eventTimedWaitNoTransition+66(event.c:72)@0x7fac47be8593
    at mmGCWorkerThread+137(gcthreads.c:809)@0x7fac47c0774a
    at thread_stub+170(lifecycle.c:808)@0x7fac47cc15bb
    at start_thread+208(:0)@0x3708c077e1
    Locked ownable synchronizers:
        - None

"(GC Worker Thread 2)" id=? idx=0x14 tid= 10500 prio=5 alive, daemon
    at pthread_cond_wait@@GLIBC_2.3.2+202(:0)@0x3708c0b44a
    at eventTimedWaitNoTransitionImpl+71(event.c:90)@0x7fac47be8528
    at eventTimedWaitNoTransition+66(event.c:72)@0x7fac47be8593
    at mmGCWorkerThread+137(gcthreads.c:809)@0x7fac47c0774a
    at thread_stub+170(lifecycle.c:808)@0x7fac47cc15bb
    at start_thread+208(:0)@0x3708c077e1
    Locked ownable synchronizers:
        - None

"(GC Worker Thread 3)" id=? idx=0x18 tid= 10501 prio=5 alive, daemon
    at pthread_cond_wait@@GLIBC_2.3.2+202(:0)@0x3708c0b44a
    at eventTimedWaitNoTransitionImpl+71(event.c:90)@0x7fac47be8528
    at eventTimedWaitNoTransition+66(event.c:72)@0x7fac47be8593
    at mmGCWorkerThread+137(gcthreads.c:809)@0x7fac47c0774a
    at thread_stub+170(lifecycle.c:808)@0x7fac47cc15bb
    at start_thread+208(:0)@0x3708c077e1
    Locked ownable synchronizers:
        - None

"(GC Worker Thread 4)" id=? idx=0x1c tid= 10502 prio=5 alive, daemon
    at pthread_cond_wait@@GLIBC_2.3.2+202(:0)@0x3708c0b44a
    at eventTimedWaitNoTransitionImpl+71(event.c:90)@0x7fac47be8528
    at eventTimedWaitNoTransition+66(event.c:72)@0x7fac47be8593
    at mmGCWorkerThread+137(gcthreads.c:809)@0x7fac47c0774a
    at thread_stub+170(lifecycle.c:808)@0x7fac47cc15bb
    at start_thread+208(:0)@0x3708c077e1
    Locked ownable synchronizers:
        - None

6.2. 找到性能瓶颈

事情到了这里,已经不难得出当前系统瓶颈就是频繁 GC。
为何会如此频繁 GC 呢?继续看 JStack,发现这两个互相矛盾的现象:
一方面 GC Worker 线程在拼命 GC,但是 GC 前后效果不明显,已用堆内存始终降不下来;
另一方面大量 ExecuteThread 业务处理线程处于 alloc_enqueue_allocation_and_wait_for_gc 的 native_blocked 阻塞状态。
此外,停止压测以后,查看已用堆内存大小,也就几百兆,不到分配堆内存的 1/10。
这说明了什么呢?这说明了我们应用里没有内存泄漏、静态对象不是太多、有大量的业务线程在频繁创建一些生命周期很长的临时对象。
很明显还是代码里有问题。那么这些对象来自哪里?如何在海量业务代码里边准确定位这些性能代码?也就是说如何利用 JVM 调优驱动代码层面的调优?请参考博客《 JVM 性能调优实战之:使用阿里开源工具 TProfiler 在海量业务代码中精确定位性能代码》,使用 TProfiler 我们成功找到了代码里边导致 JVM 频繁 GC 的元凶,并最终解决掉了这个性能瓶颈,将 TRT 降到了 0.5,TPS 提升至 100 +。

参考资料

           

给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow
这里写图片描述
你好! 这是你第一次使用 **Markdown编辑器** 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。

新的改变

我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:

  1. 全新的界面设计 ,将会带来全新的写作体验;
  2. 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
  3. 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
  4. 全新的 KaTeX数学公式 语法;
  5. 增加了支持甘特图的mermaid语法1 功能;
  6. 增加了 多屏幕编辑 Markdown文章功能;
  7. 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
  8. 增加了 检查列表 功能。

功能快捷键

撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G

合理的创建标题,有助于目录的生成

直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC语法后生成一个完美的目录。

如何改变文本的样式

强调文本 强调文本

加粗文本 加粗文本

标记文本

删除文本

引用文本

H2O is是液体。

210 运算结果是 1024.

插入链接与图片

链接: link.

图片: Alt

带尺寸的图片: Alt

当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。

如何插入一段漂亮的代码片

博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片.

// An highlighted block var foo = 'bar'; 

生成一个适合你的列表

  • 项目
    • 项目
      • 项目
  1. 项目1
  2. 项目2
  3. 项目3
  • 计划任务
  • 完成任务

创建一个表格

一个简单的表格是这么创建的:

项目Value
电脑$1600
手机$12
导管$1

设定内容居中、居左、居右

使用:---------:居中
使用:----------居左
使用----------:居右

第一列第二列第三列
第一列文本居中第二列文本居右第三列文本居左

SmartyPants

SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:

TYPEASCIIHTML
Single backticks'Isn't this fun?'‘Isn’t this fun?’
Quotes"Isn't this fun?"“Isn’t this fun?”
Dashes-- is en-dash, --- is em-dash– is en-dash, — is em-dash

创建一个自定义列表

Markdown
Text-to- HTML conversion tool
Authors
John
Luke

如何创建一个注脚

一个具有注脚的文本。2

注释也是必不可少的

Markdown将文本转换为 HTML

KaTeX数学公式

您可以使用渲染LaTeX数学表达式 KaTeX:

Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n1)!nN 是通过欧拉积分

Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t   . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=0tz1etdt.

你可以找到更多关于的信息 LaTeX 数学表达式here.

新的甘特图功能,丰富你的文章

gantt
        dateFormat  YYYY-MM-DD
        title Adding GANTT diagram functionality to mermaid
        section 现有任务
        已完成               :done,    des1, 2014-01-06,2014-01-08
        进行中               :active,  des2, 2014-01-09, 3d
        计划一               :         des3, after des2, 5d
        计划二               :         des4, after des3, 5d
  • 关于 甘特图 语法,参考 这儿,

UML 图表

可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图::

张三 李四 王五 你好!李四, 最近怎么样? 你最近怎么样,王五? 我很好,谢谢! 我很好,谢谢! 李四想了很长时间, 文字太长了 不适合放在一行. 打量着王五... 很好... 王五, 你怎么样? 张三 李四 王五

这将产生一个流程图。:

链接
长方形
圆角长方形
菱形
  • 关于 Mermaid 语法,参考 这儿,

FLowchart流程图

我们依旧会支持flowchart的流程图:

  • 关于 Flowchart流程图 语法,参考 这儿.

导出与导入

导出

如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。

导入

如果你想加载一篇你写过的.md文件或者.html文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。


  1. mermaid语法说明 ↩︎

  2. 注脚的解释 ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值