jvm attach 机制
1 jvm与外部通信方式
bin/ 命令jmap,jstack,jinfo命令是如何获取到jvm内部的信息,jvisiualvm工具是如何获取某个目标jvm信息,然后展示到界面上。
jvm内部提供了一种机制叫attach机制,jvm外部可以通过attach机制连接目标jvm内部,获取内部jvm的信息,还可以动态加载agent,修改内部类等等
2 attach机制是什么
启动一个服务,启动java visualvm 可以看到
Attach Listener
Signal Dispatcher 两个线程:
注意:signal dispatcher主要处理信号,它会默认启动,而attach listener线程不会随着jvm进程启动而启动,需要外部介入。
有两种方式可触发attach listener,第一种就是 jvm启动参数(没必要)
StartAttachListener 参数,执行命令 java -XX:+StartAttachListener
第二种方式通过signal dispatcher方式启动
看一下VirtualMachine.attach(pid)的源码
public static VirtualMachine attach(String pid) throws AttachNotSupportedException, IOException {
if (pid == null) {
throw new NullPointerException("id cannot be null");
} else {
List providerList = AttachProvider.providers();
if (providerList.size() == 0) {
throw new AttachNotSupportedException("no providers installed");
} else {
AttachNotSupportedException ex = null;
Iterator iterator = var1.iterator();
while(iterator.hasNext()) {
AttachProvider provider = (AttachProvider)iterator.next();
try {
return provider.attachVirtualMachine(pid);
} catch (AttachNotSupportedException ex2) {
ex = ex2;
}
}
throw ex;
}
}
}
AttachProvider 调用attachVirtualMachine(pid)方法,遍历providerlist,找到匹配的attachprovider(LinuxAttachProvider)对象,在创建LinuxAttachProvider对象时,会往目标jvm发送某个信号,目标jvm收到信号后,只有signal dispatcher会处理该信号,singal dispatcher收到信号后,会触发
attach listener init方法,就会启动attach listener线程。(创建临时socket文件,创建进程id文件,然后匹配等等)
// Starts the Attach Listener thread
void AttachListener() ::init{
EXCEPTION_MARK;
klassOop k = SystemDictionary::resolve_or_fail(vmSymbols::java_lang_Thread(), true, CHECK);
instanceKlassHandle klass (THREAD, k);
instanceHandle thread_oop = klass->allocate_instance_handle(CHECK);
const char thread_name[] = "Attach Listener";
Handle string = java_lang_String::create_from_str(thread_name, CHECK);
// Initialize thread_oop to put it into the system threadGroup
Handle thread_group (THREAD, Universe::system_thread_group());
JavaValue result(T_VOID);
JavaCalls::call_special(&result, thread_oop,
klass,
vmSymbols::object_initializer_name(),
vmSymbols::threadgroup_string_void_signature(),
thread_group,
string,
CHECK);
KlassHandle group(THREAD, SystemDictionary::ThreadGroup_klass());
JavaCalls::call_special(&result,
thread_group,
group,
vmSymbols::add_method_name(),
vmSymbols::thread_void_signature(),
thread_oop, // ARG 1
CHECK);
{ MutexLocker mu(Threads_lock);
JavaThread* listener_thread = new JavaThread(&attach_listener_thread_entry);
// Check that thread and osthread were created
if (listener_thread == NULL || listener_thread->osthread() == NULL) {
vm_exit_during_initialization("java.lang.OutOfMemoryError",
"unable to create new native thread");
}
java_lang_Thread::set_thread(thread_oop(), listener_thread);
java_lang_Thread::set_daemon(thread_oop());
listener_thread->set_threadObj(thread_oop());
Threads::add(listener_thread);
Thread::start(listener_thread);
}
}
3 Attach listener是如何工作的?
static void attach_listener_thread_entry(JavaThread* thread, TRAPS) {
os::set_priority(thread, NearMaxPriority);
thread->record_stack_base_and_size();
if (AttachListener::pd_init() != 0) {
return;
}
AttachListener::set_initialized();
for (;;) {
AttachOperation* op = AttachListener::dequeue();
if (op == NULL) {
return; // dequeue failed or shutdown
}
ResourceMark rm;
bufferedStream st;
jint res = JNI_OK;
// handle special detachall operation
if (strcmp(op->name(), AttachOperation::detachall_operation_name()) == 0) {
AttachListener::detachall();
} else {
// find the function to dispatch too
AttachOperationFunctionInfo* info = NULL;
for (int i=0; funcs[i].name != NULL; i++) {
const char* name = funcs[i].name;
assert(strlen(name) <= AttachOperation::name_length_max, "operation <= name_length_max");
if (strcmp(op->name(), name) == 0) {
info = &(funcs[i]);
break;
}
}
// check for platform dependent attach operation
if (info == NULL) {
info = AttachListener::pd_find_operation(op->name());
}
if (info != NULL) {
// dispatch to the function that implements this operation
res = (info->func)(op, &st);
} else {
st.print("Operation %s not recognized!", op->name());
res = JNI_ERR;
}
}
// operation complete - send result and output to client
op->complete(res, &st);
}
}
AttachOperation* AttachListener::dequeue() {
JavaThread* thread = JavaThread::current();
ThreadBlockInVM tbivm(thread);
thread->set_suspend_equivalent();
// cleared by handle_special_suspend_equivalent_condition() or
// java_suspend_self() via check_and_wait_while_suspended()
AttachOperation* op = LinuxAttachListener::dequeue();
// were we externally suspended while we were waiting?
thread->check_and_wait_while_suspended();
return op;
}
LinuxAttachOperation* LinuxAttachListener::dequeue() {
for (;;) {
int s;
// wait for client to connect
struct sockaddr addr;
socklen_t len = sizeof(addr);
RESTARTABLE(::accept(listener(), &addr, &len), s);
if (s == -1) {
return NULL; // log a warning?
}
// get the credentials of the peer and check the effective uid/guid
// - check with jeff on this.
struct ucred cred_info;
socklen_t optlen = sizeof(cred_info);
if (::getsockopt(s, SOL_SOCKET, SO_PEERCRED, (void*)&cred_info, &optlen) == -1) {
int res;
RESTARTABLE(::close(s), res);
continue;
}
uid_t euid = geteuid();
gid_t egid = getegid();
if (cred_info.uid != euid || cred_info.gid != egid) {
int res;
RESTARTABLE(::close(s), res);
continue;
}
// peer credential look okay so we read the request
LinuxAttachOperation* op = read_request(s);
if (op == NULL) {
int res;
RESTARTABLE(::close(s), res);
continue;
} else {
return op;
}
}
}
attach listener线程启动后,会不停的从队列获取AttachOperation,
AttachListener::dequeue(); 调的是
LinuxAttachListener::dequeue();
底层逻辑等待请求,当获取请求后,创建套接字,读取数据,构建一个
LinuxAttachOperation对象
看一个例子
总结: