分享一个新出炉的JVM里不痛不痒的BUGAttach机制相关

44次阅读

共计 1337 个字符,预计需要花费 4 分钟才能阅读完成。

本文来自: PerfMa 技术社区

PerfMa(笨马网络)官网

概述

老早之前写过一篇文章,关于 attach 机制的,可以看下这篇老文章了解一下 JVM 源码分析之 Attach 机制实现完全解读,比如大家常用的 jstack,jmap 等工具的主要原理都和 attach 机制有关,在 JVM 里处理这些命令的线程主要是 Attach Listener 这个线程,这个线程在 JVM 里是唯一的,我之前也一直以为是唯一的,但是我们同事最近在做一个线程分析产品的时候,发现我们抓到了多个 Attach Listener 线程,这让我也很疑惑,我第一感觉是不可能,肯定是数据抓错了,直到亲眼看到了两个同名的 Attach Listener 线程我才不得不相信原来还真有这种情况。

问题分析

不过从 Attach Listener 的实现来看,它设计的初衷不应该是一个多线程的设计,于是我昨晚上又翻了一遍代码,发现还真可能存在这种情况。举个栗子,当我们很多人同时执行 jstack 的时候,就可能会发生,当然有个前提是之前都没有做过任何和 attach 相关的操作。

Attach Listener线程默认情况下不会在 JVM 启动的时候就创建,当然也有一个 JVM 参数可以指定在 JVM 启动的时候就启动这个线程,这个就不会存在我们今天讨论的这个问题了,这个 JVM 参数是 -XX:+StartAttachListener

当我们在运行时触发 attach 机制的时候,首先会通过 Signal Dispatcher 线程来创建 Attach Listener 线程,代码如下:

在上面的圈起来的 init 方法里会创建 Attach Listener 线程,但是在 init 方法执行之前会通过 _initialized 属性来判断是否需要创建线程,而 _initialized 设置为 true 是在 attach_listener_thread_entry 里,这个是 Attach Listener Thread 的 entry,也就是当这个线程执行的时候执行的方法。

但是在设置 _initialized=true 之前,如果有多个请求信号发出了(比如同时又很多 jstack 命令触发),可能会创建多个 Attach Listener,因为Signal DispatcherAttach Listener线程是异步执行的。

问题复现

为了让效果更明显,我们可以在 hotspot 里修改下代码重新编译下再跑 demo

在上面函数里加上圈起来的这段代码,表示在设置 _initialized 属性之前停留 15s,当进程起来之后,不断执行 jstack <pid>,最终将会看到有非常多的Attach Listener 线程

其实问题的根本就是有一个空档期(设置 _initialized 为 true 之前)可能存在多次创建线程的可能。

总结

总的来说,创建 Attach Listener 线程是通过 Signal Dispatcher 线程来创建的,但是决定 Signal Dispatcher 是否可以重复创建 Attach Listener 线程的标记是在某个 Attach Listener 线程里设置的,如果没有及时设置该标记,就可能存在创建多个 Attach Listener 线程的情况。

一起来学习吧

PerfMa KO 系列课之 JVM 参数【Memory 篇】

实战:一次疑似内存泄漏的问题排查

正文完
 0