加入收藏 | 设为首页 | 会员中心 | 我要投稿 源码网 (https://www.900php.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长资讯 > 外闻 > 正文

为何服务器QPS上不去?Java线程调优权威指南

发布时间:2019-07-16 16:20:21 所属栏目:外闻 来源:今日头条
导读:副标题#e# 从刚问世起,Java 的部分魅力就来自其多线程。即便在多核和多 CPU 系统司空见惯之前,能够轻松编写多线程程序也是 Java 的一个标志性特征。 Java 性能方面的吸引力显而易见:如果有两个 CPU 可用,那么一个应用能够完成的工作量可能是原来的 2 倍

在 64 位的 JVM 中,除非物理内存非常有限,并且较小的栈可以防止耗尽原生内存,否则没有理由设置这个值。另一方面,在 32 位的 JVM 上,使用较小的栈(比如 128 KB)往往是个不错的选择,因为这样可以在进程空间中释放部分内存,使得 JVM 的堆可以大一些。

耗尽原生内存

没有足够的原生内存来创建线程,也可能会抛出 OutOfMemoryError。这意味着可能出现了以下 3 种情况之一。

在 32 位的 JVM 上,进程所占空间达到了 4 GB 的最大值(或者小于 4 GB,取决于操作系统)。

系统实际已经耗尽了虚拟内存。

在 Unix 风格的系统上,用户创建的进程数已经达到配额限制。这方面单独的线程会被看作一个进程。

减少栈的大小可以克服前两个问题,但是对第三个问题没什么效果。遗憾的是,我们无法从 JVM 报错看出到底是哪种情况,只能在遇到错误时依次排查。

要改变线程的栈大小,可以使用 -Xss=N 标志(例如 -Xss=256k)。

小结

在内存比较稀缺的机器上,可以减少线程栈大小。

在 32 位的 JVM 上,可以减少线程栈大小,以便在 4 GB 进程空间限制的条件下,稍稍增加堆可以使用的内存。

监控线程与锁

在对应用中的线程和同步的效率作性能分析时,有两点需要注意:总的线程数(既不能太大,也不能太小)和线程花在等待锁或其他资源上的时间。

1. 查看线程

几乎所有的 JVM 监控工具都提供了线程数(以及这些线程在干什么)相关的信息。像 jconsole这样的交互式工具还能显示 JVM 内线程的状态。在 jconsole 的 Threads 面板上,可以实时观察程序执行期间线程数的增减。下图是一个例子。

在某个时间点,应用(NetBeans)最多使用了 45 个线程。图中刚开始有一个爆发点,最多会使用 38 个线程,后来线程数稳定在 30 到 31 之间。jconsole 可以打印每个单独线程的栈信息;如图所示,Java2D Disposer 线程正在某个引用队列的锁上等待。

为何服务器QPS上不去?Java线程调优权威指南

JConsole 中的活跃线程视图

2. 查看阻塞线程

如果想了解应用中有什么线程在运行这类高层视图,实时线程监控会很有用,但至于那些线程在做什么,实际上没有提供任何数据。要确定线程的 CPU 周期都耗在哪儿了,则需要使用分析器(profiler)。利用分析器可以很好地观察哪些线程在执行。而且分析器一般非常成熟,可以指出那些能够通过更好的算法、更好的代码选择来加速整体执行效果的代码区域。

诊断阻塞的线程更为困难,尽管这类信息对应用的整体执行而言往往更为重要,特别是当代码运行在多 CPU 系统上,但没有利用起所有可用的 CPU 时。一般有三种执行此类诊断的方法。方法之一还是使用分析器,因为大部分分析工具都会提供线程执行的时间线信息,这就可以看到线程被阻塞的时间点。

  • 被阻塞线程与JFR

要了解线程是何时被阻塞的,迄今为止最好的方式是使用可以窥探 JVM 内部、并且可以在较低的层次确定线程被阻塞时间的工具。Java 飞行记录器(Java Flight Recorder,JFR)就是一款这样的工具。我们可以深入到 JFR 捕获的事件中,并寻找那些引发线程阻塞的事件(比如等待获取某个 Monitor,或是等待读写 Socket,不过写的情况较为少见)。

借助 JMC 的直方图面板可以很方便地查看这些事件,如下图所示。

为何服务器QPS上不去?Java线程调优权威指南

JFR 中被某个 Monitor 阻塞的线程

在这个示例中,与 sun.awt.AppContext.get 方法中的 HashMap 关联的锁被竞争了 163 次(超过 66 秒),使得所测量的请求响应时间平均增加了 31 毫秒。栈轨迹表明竞争源于 JSP 写java.util.Date 对象的方式。要改进这段代码的可伸缩性,可以使用线程局部的日期格式化对象,而不是简单地调用日期对象的 toString 方法。

从直方图中选择阻塞事件,然后检查调用代码,这个流程适合任何阻塞事件;这款与 JVM 紧密集成的工具使这一流程就成为可能。

  • 被阻塞线程与JStack

如果没有商用的 JVM 可用,替代方案之一是从程序中拿到大量的线程栈并加以检查。jstack、jcmd 和其他工具可以提供虚拟机中每个线程状态相关的信息,包括线程是在运行、等待锁还是等待 I/O 等。对于确定应用中正在进行的是什么,这可能非常有用,不过输出中也有很多我们不需要的。

在查看线程栈时,有两点需要注意。第一,JVM 只能在特定的位置(safepoint,安全点)转储出一个线程的栈。第二,每次只能针对一个线程转储出栈信息,所以可能会看到彼此冲突的信息:比如两个线程持有同一个锁,或者一个线程正在等待的锁并未被其他线程持有。

JStack 分析器

(编辑:源码网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读