文章目录

  • 一、利用Attach机制实现一个简单的Jstack
  • 二、Attach实现原理
    • Attach Listener线程的启动
      • 1. 启动的时候通过jvm参数指定启动该线程。
      • 2. attach目标JVM成功后,目标JVM启动该线程。
    • Attach Listener线程工作原理
  • 参考资料

在JVM运行时,我们经常需要获取目标JVM运行时的相关信息。最典型的一个场景就是通过jstack命令输出当前的线程dump。

对于这种场景,java提供了attach机制。通过attach机制,我们可以直接attach到目标JVM进程,然后进行一些操作,比如获取内存dump、线程dump,类信息统计(比如已加载的类以及实例个数等),动态加载agent,动态设置vm flag(但是并不是所有的flag都可以设置的,因为有些flag是在jvm启动过程中使用的,是一次性的),打印vm flag,获取系统属性等。

一、利用Attach机制实现一个简单的Jstack

在sun 包的com.sun.tools.attach下,有一系列和Attach相关的类,要attach到目标的JVM代码也很简单。下面我们利用Attach机制实现一个简单的Jstack:

    public static void main(String[] args) throws IOException, AttachNotSupportedException {VirtualMachine attach = VirtualMachine.attach("pid");List<VirtualMachineDescriptor> list = VirtualMachine.list();InputStream in = ((HotSpotVirtualMachine) attach).remoteDataDump((Object[]) args);byte b[] = new byte[256];int n = 0;do {n = in.read(b);if (n > 0) {System.out.println(new String(b, 0, n));}} while (n > 0);in.close();attach.detach();}

二、Attach实现原理

attach机制的实现涉及到了进程间的通信。那么Attach机制是如何让两个JVM进程之间可以正常通信呢

经常通过jstack查看线程dump的同学可能会留意到下面这个两个线程:

"Attach Listener" #10 daemon prio=9 os_prio=31 tid=0x00007fef2283a000 nid=0x4b03 runnable [0x0000000000000000]java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" #4 daemon prio=9 os_prio=31 tid=0x00007fef22030000 nid=0x3907 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE

这两个线程都是JVM线程。其中每个JVM都会有Signal Dispatcher线程,用于处理信号。Attach Listener线程用于JVM进程间的通信,但是它不一定会启动,启动它有两种方式。

Attach Listener线程的启动

1. 启动的时候通过jvm参数指定启动该线程。

主要涉及的参数有

JVM参数 默认值
DisableAttachMechanism false
StartAttachListener false
ReduceSignalUsage false

JVM启动Attach Listener线程的代码如下 :

if (!DisableAttachMechanism) {if (StartAttachListener || AttachListener::init_at_startup()) {AttachListener::init();}
}
bool AttachListener::init_at_startup() {if (ReduceSignalUsage) {return true;} else {return false;}
}

java -XX:+StartAttachListener mainClass即可。

2. attach目标JVM成功后,目标JVM启动该线程。

如果不在启动jvm的时候启动Attach Listener线程,那只能依靠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,这里包含了各个系统的AttachProvider实现。之后遍历这个providerList,直到匹配上。如果是在linux下,最终会调用LinuxAttachProvider,然后返回一个LinuxVirtualMachine实例。我们看一下LinuxVirtualMachine的构造方法:

LinuxVirtualMachine(AttachProvider provider, String vmid)throws AttachNotSupportedException, IOException{super(provider, vmid);int pid;try {pid = Integer.parseInt(vmid);} catch (NumberFormatException x) {throw new AttachNotSupportedException("Invalid process identifier");}//尝试获取socketFile,如果没获取到,就准备往目标JVM发送信号path = findSocketFile(pid);if (path == null) {//创建attach文件 /proc/<pid>/cwd/.attach_pid<pid> File f = createAttachFile(pid);try {//如果是linux,由于linux线程的实现是轻进程,因此需要往它的所有子进程发送信号//如果不是linux,直接往目标进程发送sigquit信号即可if (isLinuxThreads) {int mpid;try {mpid = getLinuxThreadsManager(pid);} catch (IOException x) {throw new AttachNotSupportedException(x.getMessage());}assert(mpid >= 1);sendQuitToChildrenOf(mpid);} else {sendQuitTo(pid);}int i = 0;long delay = 200;int retries = (int)(AttachTimeout() / delay);do {try {Thread.sleep(delay);} catch (InterruptedException x) { }//开始轮询等待目标JVM建立socketFilepath = findSocketFile(pid);i++;} while (i <= retries && path == null);if (path == null) {throw new AttachNotSupportedException("Unable to open socket file: target process not responding " +"or HotSpot VM not loaded");}} finally {f.delete();}}//这里需要检查目标JVM创建的socketFile我们是否有权限访问checkPermissions(path);//检查我们是否有权限连接到目标进程int s = socket();try {connect(s, path);} finally {close(s);}}

在这个方法里面,会先判断对应目录下有没有socketFile,如果没有,则往目标进程发送sigquit信号。目标JVM进程收到sigquit信号后,主要由Signal Dispatcher线程处理。Signal Dispatcher线程判断出信号是sigquit时,就会启动Attach Listener线程。

在linux中,线程是用进程的方式来实现的,也就是轻进程。因此,要发sigquit信号到Signal Dispatcher线程,就需要往该JVM进程的所有子进程发送sigquit信号。而JVM里除了vm thread,其他线程都设置了对此信号的屏蔽,因此收不到该信号。

Attach Listener线程工作原理

Attach Listener线程启动后,就会创建一个监听套接字,并创建了一个文件/tmp/.java_pid,这个就是LinuxVirtualMachine构造函数中一直尝试获取的socketFile。随着这个socketFile创建,也就意味着客户端那边的attach成功了。

之后客户端和目标JVM进程就通过这个socketFile进行通信。客户端可以通过这个socketFile发送相关命令。Attach Listener线程做的事情就是监听这个socketFile,发现有请求就解析,然后根据命令执行不同的方法,最后将结果返回。

**Unix domain socket 又叫 IPC(inter-process communication 进程间通信) socket,用于实现同一主机上的进程间通信。**socket 原本是为网络通讯设计的,但后来在 socket 的框架上发展出一种 IPC 机制,就是 UNIX domain socket。虽然网络 socket 也可用于同一台主机的进程间通讯(通过 loopback 地址 127.0.0.1),但是 UNIX domain socket 用于 IPC 更有效率:不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。这是因为,IPC 机制本质上是可靠的通讯,而网络协议是为不可靠的通讯设计的。

参考资料

JVM Attach机制实现

Unix domain socket维基百科

Unix domain socket简介

Java Attach机制简介相关推荐

  1. 一分钟了解Java Attach机制

    场景 在JVM运行时,我们经常需要获取目标JVM运行时的相关信息,jstack,jmap等常用工具的主要原理都和attach机制有关. 用法 默认不开启 可通过-XX:+StartAttachList ...

  2. Flink从入门到精通100篇(十一)-Java SPI 机制在 Flink SQL 中的应用

    Java SPI 机制简介 Java SPI机制,即Java Service Provider Interface,是Java提供的基于"接口编程 + 策略模式 + 配置文件"组合 ...

  3. java内存区域简介以及GC机制

    目录 前言 1.java内存区域简介 1.1 方法区 (Method Area) 1.2 堆区 (Heap) 1.3 程序计数器 (pc 寄存器): 1.4 Java 虚拟机栈(JVM Stacks) ...

  4. java 反射 orm_Java-反射机制简介

    什么是Java反射机制? Java反射说的是在运行状态中,对于任何一个类,我们都能够知道这个类有哪些方法和属性.对于任何一个对象,我们都能够对它的方法和属性进行调用.我们把这种动态获取对象信息和调用对 ...

  5. 双表查询java代码_什么是JDBC?Java数据库连接性简介

    JDBC(Java数据库连接性)是Java API,用于管理与数据库的连接,发出查询和命令以及处理从数据库获得的结果集.JDBC在1997年作为JDK 1.1的一部分发布,是为Java持久层开发的首批 ...

  6. Java反射机制Reflection

    Java反射机制 1 .class文件 2 Class类 3 Class类与反射机制 4 Java反射机制的类库支持及简介 5 反射机制的定义与应用 6 反射机制Demo Java反射机制demo(一 ...

  7. JVM Attach机制实现

    Attach是什么   在讲这个之前,我们先来点大家都知道的东西,当我们感觉线程一直卡在某个地方,想知道卡在哪里,首先想到的是进行线程dump,而常用的命令是jstack ,我们就可以看到如下线程栈了 ...

  8. Java GC系列(1):Java垃圾回收简介

    转载自  Java GC系列(1):Java垃圾回收简介 这篇教程是系列第一部分.首先会解释基本的术语,比如JDK.JVM.JRE和HotSpotVM.接着会介绍JVM结构和Java 堆内存结构.理解 ...

  9. java 反射机制 视频_【视频笔记】Java反射机制笔记

    Java 语言的反射机制 在Java运行时环境中,对于任意一个类,可以知道这个类有哪些属性和方法.对于任意一个对象,可以调用它的任意一个方法. 这种动态获取类的信息以及动态调用对象的方法的功能来自于J ...

  10. Java:一步步带你深入了解神秘的Java反射机制

    Java:一步步带你深入了解神秘的Java反射机制· 前言 在 Java中,反射机制(Reflection)非常重要,但对于很多开发者来说,这并不容易理解,甚至觉得有点神秘 今天,我将献上一份 Jav ...

最新文章

  1. UDP,你要耗子喂汁呀!
  2. MongoDB学习第一篇 --- Mac下使用HomeBrew安装MongoDB
  3. JavaScript的写类方式(4)——转
  4. PTA浙大版python程序设计题目集--第1章-2 从键盘输入三个数到a,b,c中,按公式值输出 (30 分)
  5. linux测试怎样看,linux入门篇:如何查看centos版本
  6. 【软件测试】黑盒测试の正交试验法
  7. 大端机,小端机;截断与提升
  8. 宫崎峻《となりのトトロ》(龙猫)全剧本(中日对照)(2)
  9. java调用fudannlp_利用FudanNLP進行新聞關鍵詞提取 | 學步園
  10. dtu MySQL_DTU-DATACENTER
  11. vue乱码图片流显示图片_vue下载二进制流图片操作
  12. 深入理解Magento第五章 – Magento资源配置
  13. 【51CTO学院三周年】初识51cto到习惯打开51cto
  14. @计算机网络基础知识
  15. 网络协议 11 - Socket 编程(下):眼见为实耳听为虚
  16. java+selenium,40行代码完成支付宝账单爬取
  17. 城市云脑,像大脑一样建设智慧城市,基于互联网云脑的新架构
  18. 你要学习,你要学习,你要学习
  19. 2018.10.25【NOIP练习】最大疯子树(树形DP)
  20. C++核心准则边译边学-目标之外

热门文章

  1. opencv中axis(轴)概念的理解
  2. Browsers简介
  3. 奇迹mu技术分享:奇迹服务端中的【DATA】文件详细说明
  4. 单机魔域显示服务器未启动,魔域单机版_开始游戏
  5. 山东大学项目实训设计系统(四)管理员端
  6. 【学习笔记】Android Fragments
  7. 1133_SICP开发环境搭建
  8. HTML技术(基础/列表/表格/表单)
  9. 常用jquery方法 总结
  10. 一种基于SoC和阿里云的智能家居系统设计方案_家电研究报告