-
Notifications
You must be signed in to change notification settings - Fork 7.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Arthas vmtool源码分析 #1920
Labels
Comments
nice job! |
3q |
Open
源码分析相关的可以打上相关label标签吗? 方便看 |
vmtool对业务应用的性能上的影响有过测试数据之类的么,如果想把这个作为一个常用的监控,比如定时使用vmtool获取一些数据,是否建议 |
vmtool getInstances默认只会拿指定类的10个实例,控制好频率和调用量,对线上影响应该较小; |
👌 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Arthas vmtool源码分析
Hello JNI
Why use JNI ?
纯Java代码不可能实现
的功能;What is JNI ?
JNI是
Java Native Interface
的缩写,通过使用native
关键字书写程序,允许Java与其他语言
进行交互。How to write application with JNI ?
step1.定义native方法
step2.生成头文件
我们使用命令生成c语言使用的
头文件
。下面是生成头文件
Main.h
的具体内容:step3.编写native的实现
MainImpl.c
step4.生成动态链接库
我的
JAVA_HOME
为/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home
,对应生成动态链接库的命令为:特别注意:
-I
要包含JAVA_HOMEinclude
文件夹下的全部文件夹,不同平台的include
子文件夹不一样;32位
的操作系统,需要把命令中的-m64
改为-m32
;动态链接库
后缀不同,比如linux是.so
,mac是.dylib
、.so
,windows是.dll
;step5.加载动态链接库
step6.调用native方法
直接像调用一个java方法一样调用它就好了,下面附上完整代码:
诚如您所见,编写一个使用了JNI的Java程序并不难!
Generic JNI
JNI shortcoming
动态链接库
交互,通常会丧失
JVM平台的可移植性,这意味着要我们自己兼容
不同的平台。Compatible JNI
在vmtool正式贡献之前,我尝试了几种方案来生成
动态链接库
:Runtime.getRuntime().exec("......")
动态生成,失败,由于安全问题,此API在生产环境直接被禁用了;vmware
并安装不同平台的虚拟机
,然后在虚拟机上打不同的动态链接库
,失败,真实原因由于个人水平有限不得而知,猜测是打包调用时最终会调用到底层的操作系统,而操作系统之间不互通;native-maven-plugin
,成功,底层仍是使用Runtime
API,只是因为打包的机器没有禁用Runtime
相关API,所以能成功;Better JNI
JDK的坑
使用
native-maven-plugin
时需要配置JDK中包含头文件的目录名(对于Oracle JDK
其实就是include
),但是对于其他JDK
可能就不是include
目录了 。怎么解决这个问题呢?
作者的做法是把不同平台的JDK都下一遍,再对它们的
include
文件夹做整合,最终才呈现给大家arthas-vmtool/src/main/native/head
。警惕内存泄露
在vmtool最初的 PR 里,调用
GetObjectsWithTags
、GetLoadedClasses
后没有释放内存
的代码,这也就导致了必定
发生的内存泄漏
,提完 PR 后,我没有注意到部分代码
存在本地方法栈内存泄漏
(不了解的同学建议阅读周志明的《深入理解Java虚拟机》),幸亏 kylixs 发现并立刻通知,才让内存泄漏问题在vmtool正式发布之前被解决,在此鸣谢。敏锐的读者可能已经察觉到了,
不是
调用所有的JVMTI方法都要编写释放内存的逻辑,那么调用JVMTI的哪些方法要编写呢?请参考JVMTI手册 。干掉不必要的回调
最开始
getInstances0
的返回结果是List<T>
而不是T[]
:这意味着要在
c
的代码中回调java的java.util.ArrayList#add
,当这种回调用达到一个量级后,能明显看到调用所耗费的时间。作者记得之前跑一个benchmark花了
5min
,干掉不必要的回调、改成返回T[]
后,再跑benchmark发现只耗费1min
了,由此可见提升是多么地巨大。Awesome vmtool
Analyze
前面铺垫了那么多,终于进入源码分析的正题了,我们以
arthas.VmTool#getInstances0
为例分析。step1.初始化JVMTI
初始化
JVMTI
(后续遍历堆
、从堆中获取类实例
和释放内存
都依赖于JVMTI
,读者可以理解为JNI
包含了JVMTI
):step2.遍历堆
我们需要获取某个类的实例怎么办?遍历堆吧。
出于性能方面的考虑,
VmTool#getInstances0
默认只会获取JVM上某个类的10
个实例,也就是说我们遍历堆,一旦发现已经有10个实例就没必要继续遍历了,那么怎么记录已遍历的实例数量
呢?借助于arthas
自定义的LimitCounter
。真正去遍历堆:
step3.从堆中获取已标记的实例
step4.把获取到的实例添加到数组
step5.释放内存并返回结果
Regret
唯一的、最大的遗憾就是
vmtool模块
不能单独使用
,如果可以单独使用的话,vmtool在获取类实例
上提供了远比Spring
强大的功能(Spring只能获取由BeanFactory
实例化的instance
,而vmtool可以获取JVM
级别的instance
)。The text was updated successfully, but these errors were encountered: