系统中有部分业务是使用C++实现,然后Java 通过 JNI 调用
但 C++ 只构建了 Windows 的 xxx.dll 和 Linux 的 libxxx.so 却没有 macOS 的 libxxx.dylib`
作为 Mac 用户,就麻烦了,一直在想个解决的办法,不然就得切换到Windows ,痛苦。
第一步解决本地运行问题
在本地开发完程序,可以发布到 docker 中,再进行功能性调试,解决本地不能运行问题。
在不涉及具体模块功能开发时,这算是解决问题了
但要经常进行代码开发这部分相关功能,就很麻烦。
不仅仅运行验证一下程序结果,还得开发调试,验证过程数据,总不能每一步都要加日志再发布 docker 调试,整体效率太低了。
最近找到了一种更好的方式:
Mac 上调试 JNI = 在 Linux 容器里跑 native + Mac IDEA 远程调试接进去。
Docker Desktop on Mac 跑的就是 Linux 容器,.so 能在里面正常加载。
实现方案
整体基于 JPDA(Java Platform Debugger Architecture)架构,通过 JDWP(Java Debug Wire Protocol)协议让外部调试器(如 IntelliJ IDEA)连接到正在运行的 JVM 上,实现远程断点调试、变量查看、单步执行等功能。
服务端增加 agent 参数
1、在发布 docker 时,容器 JVM 上加的 agent 参数就是开启 JDWP:
1 | -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 |
下面逐段解释每个参数的作用:
-agentlib:jdwp
- 指定加载 Java 调试代理库(Java Debug Wire Protocol Agent)。
- 这是 JVM 内置的调试支持模块,用于与外部调试器通信。
transport=dt_socket
- 指定调试器与 JVM 之间的通信方式为 Socket 网络传输。
- 即通过 TCP/IP 协议进行连接,支持本地或远程调试。
server=y
- 表示当前 JVM 作为 调试服务端,监听指定端口,等待调试器(客户端)来连接。
- 如果设为
server=n,则 JVM 会主动去连接调试器(较少用)。
suspend=n
- 表示 JVM 启动时 不暂停,应用程序会立即开始执行。
- 如果设为
suspend=y,JVM 会挂起,直到调试器连接后才继续执行(常用于调试启动阶段的问题)。
address=*:5005
- 指定调试器监听的 端口号为 5005。
*:表示监听 所有网络接口(即允许来自任意 IP 的连接),这是 JDK 9 及以上版本的写法。- 在 JDK 8 及以下版本中,通常写为
address=5005(只监听本地)。
- 在 JDK 8 及以下版本中,通常写为
原理:JVM 内置的 JDWP agent 暴露一个端口,IDEA 的 “Remote JVM Debug” 连上后,按 JDWP 协议下发”设断点 / 读变量 / 单步 / 取栈帧”等命令,JVM 在断点处挂起并把线程状态回传给 IDEA,于是能在 Mac 的 IDE 里像本地一样断点调试容器里的 Java 代码。
IntelliJ IDEA 接入
找到 “Remote JVM Debug” 配置入口

JPDA
JPDA(Java Platform Debugger Architecture) 是 Java 平台调试架构的缩写,通过 JPDA 提供的 API,开发人员可以方便灵活的搭建 Java 调试应用程序。
组成模块
JPDA 主要由三个部分组成:Java 虚拟机工具接口(JVMTI),Java 调试线协议(JDWP),以及 Java 调试接口(JDI)

这三个模块把调试过程分解成几个很自然的概念:调试者(debugger)和被调试者(debuggee),以及他们中间的通信器。
被调试者运行于我们想调试的 Java 虚拟机之上,它可以通过 JVMTI 这个标准接口,监控当前虚拟机的信息;
调试者定义了用户可使用的调试接口,通过这些接口,用户可以对被调试虚拟机发送调试命令,同时调试者接受并显示调试结果。
在调试者和被调试着之间,调试命令和调试结果,都是通过 JDWP 的通讯协议传输的。
所有的命令被封装成 JDWP 命令包,通过传输层发送给被调试者,被调试者接收到 JDWP 命令包后,解析这个命令并转化为 JVMTI 的调用,在被调试者上运行。类似的,JVMTI 的运行结果,被格式化成 JDWP 数据包,发送给调试者并返回给 JDI 调用。而调试器开发人员就是通过 JDI 得到数据,发出指令
JVMTI
JVMTI(Java Virtual Machine Tool Interface)即指 Java 虚拟机工具接口,它是一套由虚拟机直接提供的 native 接口,它处于整个 JPDA 体系的最底层,所有调试功能本质上都需要通过 JVMTI 来提供。通过这些接口,开发人员不仅调试在该虚拟机上运行的 Java 程序,还能查看它们运行的状态,设置回调函数,控制某些环境变量,从而优化程序性能
JDWP
JDWP = Java Debug Wire Protocol,Java 调试线协议。它是调试器和被调试 JVM 之间通信的协议规范(由 JVM TI / JVMDI 定义)。
JDI
JDI(Java Debug Interface)是三个模块中最高层的接口,在多数的 JDK 中,它是由 Java 语言实现的。 JDI 由针对前端定义的接口组成,通过它,调试工具开发人员就能通过前端虚拟机上的调试器来远程操控后端虚拟机上被调试程序的运行