在哪里寻找java中的同步争用证据?

发布于 2024-07-30 10:34:25 字数 257 浏览 1 评论 0原文

当数百个用户使用我们的 Tomcat Web 应用程序时,会感觉速度很慢。 服务器位于托管公司,他们的报告没有显示任何带宽或 CPU 工作负载问题,因此我怀疑速度下降的原因可能是由于我们在同步调用下封装的一些遗留代码存在争用,因为它是更容易的路径。

我在开发环境中做了一些人工测试,使用 ThreadLocal 解决方案更改同步调用,它变得更快,但我知道我的老板会要求我提供一些证据,证明它在生产中也会更快。

我如何确定我的应用程序中是否存在线程争用问题?

Our Tomcat web application feels slow when it is used by a couple hundred users. The servers are in a hosting company and their reports doesn't show any problem with bandwith or cpu workload, so I suspect that the reason of the slowdowns can be because of contention on some legacy code that we encapsulated under synchronized calls because it was the easier path.

I've done some artificial tests in the developement environment changing the synchronized calls with a ThreadLocal solution and it becomes faster, but I know that my boss will require me some evidence that it will also be faster in production.

How can I know for sure if thread contention is a problem in my app?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

踏雪无痕 2024-08-06 10:34:25

我认为最新 Java 6 JDK 附带的 VisualVM 工具的 线程详细信息 视图将能够为支持(或反对)你的理论提供确凿的证据。 它为每个线程显示一个饼图,显示它在运行、睡眠、等待和监视器中花费了多少时间。 最后一个(以红色显示)是您感兴趣的内容:

alt text

I think the thread details view of the visualVM tool that comes with recent Java 6 JDKs will be able to provide solid evidence for (or against) your theory. It displays a pie chart for each thread, showing how much time it's spending running, sleeping, waiting and in a monitor. The last (displayed in red) is what you're interested in:

alt text

蓝眼泪 2024-08-06 10:34:25

如果您有一个您认为更快的修改版本,请使用一些负载测试器对其进行测试(例如 JMeter )来测试两个版本。 如果存在显着差异,您将有结果来证明这一点。

If you have a modified version you think is faster, test it by using some load tester (e.g. JMeter) to test both versions. If there is a significant difference, you'll have the results to prove it.

昔日梦未散 2024-08-06 10:34:25

您可以使用许多开源 Java 分析器,以及其他可能需要付费,例如 YourKit。 您应该使用现有代码和增强代码运行测试。 一般而言,使用 ThreadLocals 应该会减少争用,但请考虑在开始优化之前进行基准测试也是有好处的。

另一个无需设置任何分析器即可完成的非常简单的测试是在应用程序显得缓慢时进行一些线程转储(ctrl-break 或kill-QUIT)。 在短时间内发现一些线程在相似或相同的监视器上等待可能会非常清楚地指出慢点。 您可以使用 TDA 等工具(Java 线程转储分析器)来帮助您梳理线程转储。

同样,在开始优化之前完成这项工作是一个好主意。 这是因为,尽管优化可能会在一些明显的地方产生影响,但实际的用户行为可能会触发开发人员没有考虑到的路径,而这些可能会成为真正的问题区域。

Thare are a bunch of open source java profilers at your disposal, as well as others that might cost money, like YourKit. You should run tests with both the existing code and your enhanced code. The work with ThreadLocals should reduce contention in general, but consider that it is also good to do the benchmarking before you begin optimization.

Another pretty simple test that can be done without setting up any profiler is to take a few thread dumps (ctrl-break or kill -QUIT) while the app appears slow. Spotting a few threads waiting on similar or same monitors over a short peroid of time might point out the slow spots pretty clearly. You can use tools like TDA, a Java thread dump analyzer to help you comb through the thread dump.

Again, doing this work before you begin optimizing is a good idea. This is because, although there may be some obvious places where optimizations could make a difference, actual user behavior might trigger paths that the developer doesnt consider, and these might turn out to be the real problem areas.

傻比既视感 2024-08-06 10:34:25
jstack PID

将打印出带有进程 ID PID 的 JVM 状态列表,以及有关线程状态的信息。

示例输出(摘录):

"AWT-XAWT" daemon prio=10 tid=0x0000000000e5f800 nid=0x476d runnable [0x00007f1a75616000..0x00007f1a75616bf0]
   java.lang.Thread.State: RUNNABLE
    at sun.awt.X11.XToolkit.waitForEvents(Native Method)
    at sun.awt.X11.XToolkit.run(XToolkit.java:543)
    at sun.awt.X11.XToolkit.run(XToolkit.java:518)
    at java.lang.Thread.run(Thread.java:636)

"Java2D Disposer" daemon prio=10 tid=0x0000000000d8b800 nid=0x476c in Object.wait() [0x00007f1a759df000..0x00007f1a759dfc70]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00007f1a82e2c3f8> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:133)
    - locked <0x00007f1a82e2c3f8> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:149)
    at sun.java2d.Disposer.run(Disposer.java:143)
    at java.lang.Thread.run(Thread.java:636)

我还尝试隔离争用的资源。 例如,如果遗留库正在锁定以同步对数据库的写入,那么您可能会最小化写入。

jstack PID

will print out a listing of the status of the JVM with process id PID, along with information on thread statuses.

sample output (excerpt):

"AWT-XAWT" daemon prio=10 tid=0x0000000000e5f800 nid=0x476d runnable [0x00007f1a75616000..0x00007f1a75616bf0]
   java.lang.Thread.State: RUNNABLE
    at sun.awt.X11.XToolkit.waitForEvents(Native Method)
    at sun.awt.X11.XToolkit.run(XToolkit.java:543)
    at sun.awt.X11.XToolkit.run(XToolkit.java:518)
    at java.lang.Thread.run(Thread.java:636)

"Java2D Disposer" daemon prio=10 tid=0x0000000000d8b800 nid=0x476c in Object.wait() [0x00007f1a759df000..0x00007f1a759dfc70]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00007f1a82e2c3f8> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:133)
    - locked <0x00007f1a82e2c3f8> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:149)
    at sun.java2d.Disposer.run(Disposer.java:143)
    at java.lang.Thread.run(Thread.java:636)

I'd also try to isolate the resource under contention. e.g. if the legacy library is locking to syncrhonize writes to a database maybe you'd miniimize the writes.

为人所爱 2024-08-06 10:34:25

你的分析听起来很有道理。 您能否将 VisualVM(在 JDK 中)附加到进程中,以便您可以看到时间花在哪里?

Your analysis sound reasonable. Can you attach e.g. visualvm (in the JDK) to the processes so you can see where the time is spent?

无声情话 2024-08-06 10:34:25

我可以像这样在同步调用中添加日志记录,

//...
long t0 = System.currentTimeMillis();
synchronized(lockObj){
    logger.info("T sync :" + (t0 - System.currentTimeMillis()));
    //...
}

但这感觉既便宜又肮脏。

I could add logging in our synchronized calls like this

//...
long t0 = System.currentTimeMillis();
synchronized(lockObj){
    logger.info("T sync :" + (t0 - System.currentTimeMillis()));
    //...
}

but this feels cheap and dirty.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文