当您需要从JVM内部收集数据时,您会发现自己很危险地接近Java虚拟机内部进行工作。 幸运的是,有一些方法可以避免被JVM实现细节所困扰。 Java之父没有给您提供过两个漂亮的工具供您使用。

在这篇文章中,我们将说明两种方法之间的差异,并说明为什么我们最近移植了算法的重要部分。

Java代理

第一种选择是使用java.lang.instrument接口。 这种方法使用-javaagent启动参数将监视代码加载到JVM本身。 作为Java的全部选择,如果您的背景是Java开发,那么javaagents往往是首选的方法。 说明您如何从该方法中受益的最佳方法是通过示例。

让我们创建一个真正简单的代理,该代理将负责监视代码中的所有方法调用。 当代理面对方法调用时,它将调用记录到标准输出流中:

import org.objectweb.asm.*;public class MethodVisitorNotifyOnMethodEntry extends MethodVisitor {public MethodVisitorNotifyOnMethodEntry(MethodVisitor mv) {super(Opcodes.ASM4, mv);mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(MethodVisitorNotifyOnMethodEntry.class), "callback", "()V");}public static void callback() {System.out.println("Method called!");    }
}

您可以使用上面的示例,将其打包为javaagent(本质上是一个带有特殊MANIFEST.MF的小JAR文件),然后使用类似于以下内容的代理的premain()方法启动它:

java -javaagent:path-to/your-agent.jar com.yourcompany.YourClass

启动后,您会看到一堆“方法调用!” 日志文件中的消息。 就我们而言,仅此而已。 但是这个概念很强大,尤其是与上面的示例中的字节码检测工具(例如ASM或cgLib)结合使用时。

为了使示例易于理解,我们跳过了一些细节。 但这是相对简单的-使用java.lang.instrument包时,您首先编写自己的代理类,并实现public static void premain(String agentArgs,Instrumentation inst) 。 然后,您需要向inst.addTransformer注册您的ClassTransformer 。 您很可能希望避免直接对类字节码进行操作,因此可以使用一些字节码操作库,例如我们所使用的示例中的ASM。 有了它,您只需要实现几个接口– ClassVisitor (为简便起见,略过)和MethodVisitor。

JVMTI

第二种方法最终将带您进入JVMTI。 JVM工具接口( JVM TI )是标准的本机API,允许本机库捕获事件并控制Java虚拟机。 对JVMTI的访问通常打包在称为代理的特定库中。

下面的示例演示了与javaagent部分相同的回调注册,但是这次将其实现为JVMTI调用:

void JNICALL notifyOnMethodEntry(jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread, jmethodID method) {fputs("method was called!\n", stdout);
}int prepareNotifyOnMethodEntry(jvmtiEnv *jvmti) {jvmtiError error;jvmtiCapabilities requestedCapabilities, potentialCapabilities;memset(&requestedCapabilities, 0, sizeof(requestedCapabilities));if((error = (*jvmti)->GetPotentialCapabilities(jvmti, &potentialCapabilities)) != JVMTI_ERROR_NONE) return 0;if(potentialCapabilities.can_generate_method_entry_events) {requestedCapabilities.can_generate_method_entry_events = 1;}else {//not possible on this JVMreturn 0;}if((error = (*jvmti)->AddCapabilities(jvmti, &requestedCapabilities)) != JVMTI_ERROR_NONE) return 0;jvmtiEventCallbacks callbacks;memset(&callbacks, 0, sizeof(callbacks));callbacks.MethodEntry = notifyOnMethodEntry;if((error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks))) != JVMTI_ERROR_NONE) return 0;if((error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,    JVMTI_EVENT_METHOD_ENTRY, (jthread)NULL)) != JVMTI_ERROR_NONE) return 0;return 1;
}

两种方法之间存在一些差异。 例如,通过JVMTI,您可以获得比代理更多的信息。 但是,两者之间最关键的区别在于加载机制。 在将Instrumentation代理加载到堆内部时,它们由同一JVM进行管理。 JVMTI代理不受JVM规则支配,因此不受JVM内部组件(如GC或运行时错误处理)的影响。 这意味着什么,最好通过我们自己的经验来解释。

从-javaagent迁移到JVMTI

三年前,当我们开始构建内存泄漏检测器时,我们并没有过多地关注这些方法的优缺点。 我们毫不犹豫地将解决方案实现为-javaagent

多年来,我们已经开始理解含义。 其中一些不太令人满意,因此在我们的最新版本中,我们将内存泄漏检测机制的重要部分移植到了本机代码。 是什么使我们跳到这样的结论?

首先-当驻留在堆中时,您需要将自己容纳在应用程序自己的内存结构旁边。 通过痛苦的经验中学到的东西本身就可能导致问题。 当您的应用程序已将堆填满到最大程度时,您最后需要做的是一个内存泄漏检测器,它似乎只会加快OutOfMemoryError的到达速度。

但是增加的堆空间减少了困扰我们的弊端。 真正的问题与以下事实有关:使用受监视的应用程序本身正在使用的同一垃圾收集器清理了我们的数据结构。 这导致更长或更频繁的GC暂停。

尽管大多数应用程序不介意我们为堆消耗添加的几个额外的百分点,但我们了解到,需要消除对Full GC暂停产生不可预测的影响。

更糟糕的是– Plumbr的工作方式是监视所有对象的创建和集合。 监视某物时,您需要保持跟踪。 跟踪往往会创建对象。 创建的对象将有资格使用GC。 现在,当您监视的是GC时,您刚刚创建了一个恶性循环-收集到更多的对象的垃圾,创建的监视器越多,触发的GC运行频率越高,等等。

跟踪对象时,JVMTI会通知我们有关对象死亡的信息。 但是,JVMTI不允许在这些回调期间使用JNI。 因此,如果我们使用Java保留有关跟踪对象的统计信息,则当我们收到更改通知时,不可能立即更新统计信息。 相反,当我们知道JVM处于正确状态时,需要将更改缓存并应用。 这造成了不必要的复杂性和更新实际统计数据的延迟。

翻译自: https://www.javacodegeeks.com/2014/03/migrating-from-javaagent-to-jvmti-our-experience.html

从javaagent迁移到JVMTI:我们的经验相关推荐

  1. jvmti_从javaagent迁移到JVMTI:我们的经验

    jvmti 当您需要从JVM内收集数据时,您会发现自己很危险地接近Java虚拟机内部进行工作. 幸运的是,有一些方法可以避免被JVM实现细节所困扰. Java之父没有给您提供过两个漂亮的工具供您使用. ...

  2. -javaagent:_从javaagent迁移到JVMTI:我们的经验

    -javaagent: 当您需要从JVM内收集数据时,您会发现自己很危险地接近Java虚拟机内部进行工作. 幸运的是,有一些方法可以避免被JVM实现细节所困扰. Java之父不仅给您提供了两个漂亮的工 ...

  3. T430系统盘迁移到SSD上的经验

    前提:win10,不想重新安装众多的软件,希望系统能无缝移植到SSD上. 1.光驱位放置SSD: 2.微PE启动,分区助手迁移系统盘到SSD: 3.关机,去掉机械硬盘: 4.重启提示:由于硬件改动启动 ...

  4. 记一次小程序接口从云开发迁移至本地服务器的经验收获

    文章目录 前言 项目背景 一.上手小程序 二.主要问题 1.请求库相关 问题描述 解决过程 2.openid相关 3.域名证书相关 总结 前言 本文章仅记录个人在第一个小程序项目中的获得的心得体会,并 ...

  5. 使用VMware Convert迁移Windows主机到虚拟机经验

    前几天在给一个单位,将其中的7台Windows Server 2003 R2的服务器(上面跑SQL Server.网站)迁移到虚拟机中,使用VMware Convert 3.02就可以很容易迁移,主要 ...

  6. 吐血整理 | 快速学习大厂们的软件案例经验

    大家好,我是煎鱼. 前几天,很怕冷的煎鱼去了趟北京,参加了为期三天的全球软件案例研究峰会(TOP 100). 同时记了大量笔记,整理后分享出来,希望对大家有所帮助,拓展眼界非常重要. 内容比较多(已经 ...

  7. 一部分 数据 迁移_软件测试员12小时惊魂记:数据库迁移出大事故,如何测试?...

    信息时代,随着用户数量不断增加,业务量不断增长,企业原有数据库不足以有效支撑业务的发展,在此情况下,企业更多的是寻求一款更加稳定的数据库进行替代. 本文以Sybase数据库和Oracle数据库为例.O ...

  8. 从 Hive 大规模迁移作业到 Spark 在有赞的实践

    编者荐语: 本文介绍了有赞迁移 Hive 做到到 SparkSQL 上所做的一些改进,以及如何做到 SparkSQL 占比提升到 91% 以上,最后也分享一些在 Spark 踩过的坑和经验希望能帮助到 ...

  9. 有故事、有实践,谈一谈深度迁移学习方法的基本思路

    百度前首席科学家.斯坦福大学副教授吴恩达(Andrew Ng)曾经说过:迁移学习将是继监督学习之后的下一个促使机器学习成功商业化的驱动力. 本文选自刚刚出版的新书<深度学习500问:AI工程师面 ...

最新文章

  1. 后赛门铁克时代Veritas加强数据保护应对欧盟法规
  2. 2017.0613.《计算机组成原理》总线控制-通信控制
  3. Spring 学习一 @Autowired
  4. (转)刘巍然-关于公钥与私钥
  5. 解决Ubuntu 22.04 LTS作为nfs server时根文件系统挂载失败的问题
  6. 计算机不显示验证码,浏览器中网页验证码不显示的原因与解决办法
  7. 淘宝封杀返现模式 淘宝客返利网站模式遇挑战
  8. 蓝桥杯:跳蚂蚱【BFS】【Python】
  9. Adobe Photoshop CS6图片无法拖入解决方法
  10. linux svn 查看忽略文件
  11. 上项线体表位置_颅骨体表定位标志
  12. 电脑里照片想要长期储存,用什么方式?
  13. 小米运动改步数不同步的原因找到了,尽然是这问题
  14. 【CS224n-5】Linguistic Structure: Dependency Parsing
  15. Foxit_PDF_SDK.NET.7.X优化插件XFA的改进
  16. 超声波脉冲发生器可调电源设计
  17. php 长链接转为短链接,网站中URL长链接转换为短链接的接口
  18. java list最后一个元素_Java 8 Stream List 获取最后一个元素
  19. OPENGL 太阳系
  20. 数据库bak文件怎么打开

热门文章

  1. CV中多的空格导致报错
  2. java 取随机正整数_Java获取随机数
  3. 互换性与技术测量教材pdf_【检验】临床生物化学检验技术(第6版)人民卫生出版社【电子教材PDF】【人卫教材电子版】...
  4. ubuntu安装python3.8_Ubuntu 16.04 安装 python3.8
  5. nginx应用领域分类+事件模型
  6. layui绑定json_JSON-B非对称属性绑定
  7. java使用泛型后消除泛型_如何以及何时使用泛型
  8. sap寄售退货单_多个退货单
  9. vert.x_选择Vert.x的3个理由
  10. javafx 自定义控件_JavaFX自定义控件– Nest Thermostat第3部分