jvmti_拥有您的堆:使用JVMTI迭代类实例

jvmti

今天,我想谈一谈我们大多数人每天都不会看到和使用的另一种Java,更确切地说,是有关较低级别的绑定,一些本机代码以及如何执行一些小的魔术。 尽管我们不会在JVM上找到真正的魔力源,但是在单个帖子的范围内可以实现一些小奇迹。

我花了很多时间在ZeroTurnaround的RebelLabs团队中进行研究,编写和编码,该公司为Java开发人员创建工具,这些工具主要以javaagents的身份运行。 通常情况下,如果您想在不重写JVM的情况下增强JVM或在JVM上获得任何强大的功能,则必须深入研究Java代理的美丽世界。 它们有两种形式:Java javaagents和本机Javaagents。 在这篇文章中,我们将集中讨论后者。


注意, XRebel产品负责人Anton Arhipov的这个GeeCON Prague演示文稿是学习完全用Java编写的Javaagents的一个很好的起点: 与Javassist一起玩 。

在本文中,我们将创建一个小型的本机JVM代理,探讨将本机方法公开到Java应用程序中的可能性,并了解如何利用Java虚拟机工具接口 。

如果您正在寻找帖子的实用内容,我们将能够在扰乱警报的情况下计算堆中存在给定类的实例数量。

想象一下,您是圣诞老人值得信赖的黑客精灵,而这位大人物对您来说面临以下挑战:

圣诞老人: 我亲爱的Hacker Elf,您能否编写一个程序来指出JVM堆中当前隐藏了多少个Thread对象?

另一个不愿意挑战自己的小精灵会回答: 这很容易直接,对吗?

return Thread.getAllStackTraces().size();

但是,如果我们想对我们的解决方案进行过度设计以能够回答有关任何给定类的问题,该怎么办? 说我们要实现以下接口?

public interface HeapInsight {int countInstances(Class klass);
}

是的,那是不可能的,对吧? 如果您收到String.class作为参数怎么办? 不用担心,我们只需要更深入地研究JVM的内部结构。 JVMTI作者可以使用的一件事是JVMTI (Java虚拟机工具接口)。 它是很久以前添加的,许多看似神奇的工具都在使用它。 JVMTI提供了两件事:

  • 本机API
  • 一种工具API,用于监视和转换装入JVM的类的字节码。

就我们的示例而言,我们需要访问本机API。 我们要使用的是IterateThroughHeap函数,该函数使我们可以提供一个自定义回调,以对给定类的每个对象执行该回调。

首先,让我们创建一个本地代理,该代理将加载和回显某些内容,以确保我们的基础架构能够正常工作。

本机代理程序是用C / C ++编写的,并被编译成动态库,以便在我们甚至开始考虑Java之前就进行加载。 如果您不精通C ++,请不要担心,没有很多精灵,也不会很难。 我的C ++方法包括2种主要策略:巧合编程和避免段错误。 因此,由于我设法编写了这篇文章的示例代码并对其进行了注释,因此我们可以一起研究一下。 注意:以上段落应作为免责声明,请勿将此代码置于任何对您有价值的环境中。

这是创建第一个本机代理的方法:

#include 
#include using namespace std;JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved)
{cout << "A message from my SuperAgent!" << endl;return JNI_OK;
}

该声明的重要部分是声明一个名为Agent_OnLoad的函数,该函数遵循动态链接的代理的文档 。

将文件另存为例如native-agent.cpp ,让我们看看我们可以做些什么来变成一个库。

我在OSX上,因此我使用clang对其进行编译,以节省一些时间,下面是完整的命令:

clang -shared -undefined dynamic_lookup -o agent.so -I /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/include/ -I /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/include/darwin native-agent.cpp

这将创建一个agent.so文件,该文件是可以为我们服务的库。 为了测试它,让我们创建一个虚拟的hello world Java类。

package org.shelajev;
public class Main {public static void main(String[] args) {System.out.println("Hello World!");}
}

当你用正确的-agentpath选项指向agent.so运行它,你应该看到下面的输出:

java -agentpath:agent.so org.shelajev.Main
A message from my SuperAgent!
Hello World!

很好! 现在,我们拥有一切使之真正有用的地方。 首先,我们需要一个jvmtiEnv实例,当我们位于Agent_OnLoad中时 ,可以通过JavaVM * jvm获得该实例 ,但以后将不可用。 因此,我们必须将其存储在全球可访问的位置。 我们通过声明一个全局结构来存储它。

#include 
#include using namespace std;typedef struct {jvmtiEnv *jvmti;
} GlobalAgentData;static GlobalAgentData *gdata;JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved)
{jvmtiEnv *jvmti = NULL;jvmtiCapabilities capa;jvmtiError error;// put a jvmtiEnv instance at jvmti.jint result = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_1);if (result != JNI_OK) {printf("ERROR: Unable to access JVMTI!\n");}// add a capability to tag objects(void)memset(∩a, 0, sizeof(jvmtiCapabilities));capa.can_tag_objects = 1;error = (jvmti)->AddCapabilities(∩a);// store jvmti in a global datagdata = (GlobalAgentData*) malloc(sizeof(GlobalAgentData));gdata->jvmti = jvmti;return JNI_OK;
}

我们还更新了代码,以添加标记对象的功能,这是我们遍历堆所需的。 现在准备工作已经完成,我们已经初始化了JVMTI实例并且可供我们使用。 让我们通过JNI将其提供给我们的Java代码。

JNI代表Java本机接口 ,这是将本机代码调用包含到Java应用程序中的一种标准方式。 Java部分将非常简单明了,将以下countInstances方法定义添加到Main类:

package org.shelajev;public class Main {public static void main(String[] args) {System.out.println("Hello World!");int a = countInstances(Thread.class);System.out.println("There are " + a + " instances of " + Thread.class);}private static native int countInstances(Class klass);
}

为了适应本机方法,我们必须更改本机代理代码。 我将在稍后解释,但现在在其中添加以下函数定义:

extern "C"
JNICALL jint objectCountingCallback(jlong class_tag, jlong size, jlong* tag_ptr, jint length, void* user_data) 
{int* count = (int*) user_data;*count += 1; return JVMTI_VISIT_OBJECTS;
}extern "C"
JNIEXPORT jint JNICALL Java_org_shelajev_Main_countInstances(JNIEnv *env, jclass thisClass, jclass klass) 
{int count = 0;jvmtiHeapCallbacks callbacks;
(void)memset(&callbacks, 0, sizeof(callbacks));
callbacks.heap_iteration_callback = &objectCountingCallback;jvmtiError error = gdata->jvmti->IterateThroughHeap(0, klass, &callbacks, &count);return count;
}

Java_org_shelajev_Main_countInstances在这里更有趣,它的名称遵循约定,以Java_开头,然后是_分隔的完全限定的类名,然后是Java代码中的方法名。 另外,请不要忘记JNIEXPORT声明,该声明指出该函数已导出到Java世界中。

Java_org_shelajev_Main_countInstances内部,我们将objectCountingCallback函数指定为回调,并使用Java应用程序中的参数调用IterateThroughHeap

请注意,我们的本机方法是静态的,因此C对应项中的参数为:

JNIEnv *env, jclass thisClass, jclass klass

对于实例方法,它们将有所不同:

JNIEnv *env, jobj thisInstance, jclass klass

这里的thisInstance指向Java方法调用的this对象。

现在, objectCountingCallback的定义直接来自文档 。 身体无非就是增加一个int。

繁荣! 全做完了! 感谢您的耐心等待。 如果您仍在阅读本文,则可以测试上面的所有代码。

再次编译本机代理并运行Main类。 这是我看到的:

java -agentpath:agent.so org.shelajev.Main
Hello World!
There are 7 instances of class java.lang.Thread

如果我添加一个线程t = new Thread(); 行到main方法,我在堆上看到8个实例。 听起来好像真的可行。 您的线程数几乎肯定会有所不同,不用担心,这是正常现象,因为它确实计入了JVM簿记线程,进行编译,GC等操作。

现在,如果我要计算堆上String实例的数量,只需更改参数类即可。 我希望圣诞老人是一个真正通用的解决方案。

哦,如果您有兴趣,它会为我找到2423个String实例。 对于小型应用程序来说,这个数字相当高。 也,

return Thread.getAllStackTraces().size();

给我5个而不是8个,因为它不包括簿记线程! 谈论琐碎的解决方案,是吗?

现在,您已经掌握了这些知识,并且知道了本教程,并不是说您已经准备好编写自己的JVM监视或增强工具,但这绝对是一个开始。

在本文中,我们从零开始编写了本机Java代理,该代理成功编译,加载和运行。 它使用JVMTI来获取无法通过其他方式访问的JVM的见解。 相应的Java代码调用本机库并解释结果。

这通常是最神奇的JVM工具所采用的方法,我希望其中的一些魔术已为您揭开神秘面纱。

翻译自: https://www.javacodegeeks.com/2014/12/own-your-heap-iterate-class-instances-with-jvmti.html

jvmti

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/338645.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

babylonjs 分部加载模型_BabylonJS加载OBJ或STL模型文件实例

JavaScript语言&#xff1a;JaveScriptBabelCoffeeScript确定var canvas document.getElementById("renderCanvas");var engine new BABYLON.Engine(canvas, true);var createScene function() {// This creates a basic Babylon Scene object (non-mesh)var scen…

骚操作:利用强弱符号制作插件库

当有强符号和弱符号时&#xff0c;选择使用强符号那么我们正可以利用这个原则做以下事情&#xff1a;定义为弱符号&#xff0c;如果是弱符号&#xff0c;使用默认行为如果链接了库&#xff0c;是强符号&#xff0c;则使用外部定义行为以此来实现一个类似插件的功能。通俗一点说…

c语言里 t是什么作用,c语言里的\t是什么意思

c语言里的&#xff3c;t是什么意思以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;c语言里的&#xff3c;t是什么意思正宗叫法是“水平制表符”&#xff0c;就是在输出媒体上水平跳过多个空格…

boot jersey_Jersey和Spring Boot入门

boot jersey除了许多新功能&#xff0c;Spring Boot 1.2还带来了Jersey支持。 这是吸引喜欢标准方法的开发人员的重要一步&#xff0c;因为他们现在可以使用JAX-RS规范构建RESTful API&#xff0c;并将其轻松部署到Tomcat或任何其他Springs Boot支持的容器中。 带有Spring平台的…

mime类型是什么 node_Node.js - 文件系统获取文件类型

我需要借助node.js来获取文件的文件类型以设置内容类型。我知道我可以很容易地检查文件扩展名&#xff0c;但我也有没有扩展名的文件&#xff0c;其内容类型应该是image/png,text/html aso。Node.js - 文件系统获取文件类型这是我的代码(我知道这并没有太大的意义&#xff0c;但…

函数或全局变量重复定义时会怎样?

可能有些朋友第一反应是&#xff0c;那肯定是编译不过喽&#xff1a;// fun.c #include void func() {printf("编程珠玑\n"); }// main.c #include void func() {printf("公众号\n"); } int main(void) {func();return 0; }编译&#xff1a;$ gcc -o main …

c语言开源编辑器,一个C语言编写的跨平台C语言编译器(开源) UCC

很多科班出身的搞开发的同事大都应该学过《编译原理》这门课程&#xff0c;或许也动手做过一些实践。这次向大家推荐一个由清华大学学生完成的C语言编译器实现 -- UCC。这个项目目前位于sf.net网站&#xff0c;开放源代码&#xff0c;代码使用C语言编写&#xff0c;对于理解和实…

java持久性与安全性_Java持久性锁定初学者指南

java持久性与安全性隐式锁定 在并发理论中&#xff0c;锁定用于保护可变共享数据免受危险数据完整性异常的影响。 因为锁管理是一个非常复杂的问题&#xff0c;所以大多数应用程序都依赖于其数据提供程序隐式锁技术。 将整个锁定职责委托给数据库系统既可以简化应用程序开发&a…

c语言实验报告5数组,c语言实验报告五一维数组.doc

c语言实验报告五一维数组《C程序设计》课程实验报告学院&#xff1a; 班级&#xff1a; 姓名&#xff1a; 学号&#xff1a; 实验设备&#xff1a;计算机1台2011年3月1日实验项目名称一维数组掌握一维数组的定义、初始化&#xff0c;以及一维数组元素的下标法引用。实验要求&am…

当C语言函数执行成功时,返回1和返回0究竟哪个好?

基本上&#xff0c;没有人会将大段的C语言代码全部塞入 main() 函数。更好的做法是按照复用率高&#xff0c;耦合性低的原则&#xff0c;尽可能的将代码拆分不同的功能模块&#xff0c;并封装成函数。C语言代码的组合千变万化&#xff0c;因此函数的功能可能会比较复杂&#xf…

ubuntu自定义安装里怎么选_超市里的五香粉怎么选?看懂配料表,两个小技巧,不怕选不好。...

点击右上角【关注】&#xff0c;可获得本头条号推荐的更多美食干货五香粉是家庭常用的一种调味料&#xff0c;焖、炖、烧、煮、卤都常备使用&#xff0c;如今市场上的五香粉品牌也是相当繁多&#xff0c;质量也是参差不齐&#xff0c;有时买到的五香粉烧制的菜肴根本就不好吃&a…

原来C语言还可以这样实现“泛型编程”!

在回答标题问题之前&#xff0c;先了解下什么是泛型编程。泛型编程&#xff08;generic programming&#xff09;是程序设计语言的一种风格或范式。泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型&#xff0c;在实例化时作为参数指明这些类型。C支持…

javadoc 标签_新的Javadoc标签@ apiNote,@ implSpec和@implNote

javadoc 标签如果您已经在使用Java 8&#xff0c;则可能会看到一些新的Javadoc标签&#xff1a; apiNote &#xff0c; implSpec和implNote 。 他们怎么了&#xff1f; 如果要使用它们&#xff0c;该怎么办&#xff1f; 总览 该帖子将快速查看标签的来源和当前状态。 然后&…

c语言万年历附加功能,万年历(c语言,多功能).doc

万年历(c语言,多功能)#include "time.h" /*包含的头文件*/#include "stdio.h"#include "math.h"#include "windows.h"#include "conio.h"#define KEYNUMUp 0x48 /*宏定义*/#define KEYNUMDown 0x50#define KEYNUMLeft 0x4b#…

联想小新300加固态_就联想小新 Pro 13 固态硬盘混用一事,官方回应

IT之家11月3日消息 近日有用户反馈称&#xff0c;联想小新Pro 13的部分批次固态硬盘从三星PM981A换成亿联了&#xff0c;因此还出现了退货现象。针对上述情况&#xff0c;联想官方刚刚发表声明回应此事。以下为联想回应全文&#xff1a;有关小新Pro 13固态硬盘(SSD)配置问题的公…

C语言入门基础之输入和输出

标准输入函数在stdio.h中scanf声明如下&#xff1a;/* Read formatted input from stdin.This function is a possible cancellation point and therefore notmarked with __THROW. */ extern int scanf (const char *__restrict __format, ...) __wur;使用Mac或Linux的同学&am…

camel java_与Java EE和Camel的轻量级集成

camel javaEnterprise Java具有不同的风格和观点。 从简单的平台技术&#xff08;众所周知的Java EE&#xff09;开始&#xff0c;到不同的框架和集成方面&#xff0c;最后是涉及以数据为中心的用户界面或特定可视化效果的用例。 Java EE本身无法解决的最突出的问题是“集成”。…

c语言 伪随机数程序,C语言的伪随机数

一直想好好的系统的学习一下C语言的伪随机数&#xff0c;今天终于逮到机会了伪随机数C语言中有可以产生随机数据的函数&#xff0c;需要添加stdlib.h和time.h头文件。首先在main函数开头加上srand(unsigned)time(NULL))。先来介绍一下srand头文件&#xff1a;定义函数&#xff…

最大隶属度原则_模糊数学笔记:六、模糊模型识别-I(最大隶属度原则)

1、模型识别的问题提出模型识别&#xff0c;通俗地理解即是对一个类别未知的对象进行归类&#xff08;或者叫分类&#xff09;。这里与聚类不同的是&#xff0c;聚类实际上是要区分出已有的样本哪些属于同一类&#xff0c;但并没有参考标准。而识别则事先有参考的标准&#xff…

C语言经典题

C 库函数 - tanh()描述C 库函数 double tanh(double x) 返回 x 的双曲正切。声明下面是 tanh() 函数的声明。double tanh(double x)参数x -- 浮点值。返回值该函数返回 x 的双曲正切。实例下面的实例演示了 tanh() 函数的用法。#include#include int main (){ double x, ret;…