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

jvmti_拥有您的堆:使用JVMTI迭代类实例相关推荐

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

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

  2. Python(24)-面向对象3-可迭代类对象Pokemon

    面向对象3-Pokemon demo 1.可迭代类对象 1.可迭代类对象 想要实现类对象中某些属性/数值的迭代访问,需要在类中定义一个__iter__()方法,和__next__() 的方法(pyth ...

  3. 这笔钱领了吗?拥有信息系统项目管理师等软考类证书可获技能提升补贴最高2000元!

    这笔钱,您领了吗? 根据部分省份的技能提升补贴政策,拥有信息系统项目管理师等软考类证书在满足相应条件下可申领技能提升补贴!一般来说按高级.中级.初级来分,补贴标准分别为2000元.1500元.1000 ...

  4. linux堆内存管理深入分析,【技术分享】Linux堆溢出之Fastbin Attack实例详解

    1. 摘要 在近几年各大CTF比赛中,看到有很多次pwn类别题中出现fastbin攻击的情况,例如今年的defcon,RCTF,胖哈勃杯,0CTF final等等 ,fastbin attack是堆漏 ...

  5. python牛顿迭代公式_python计算牛顿迭代多项式实例分析

    本文实例讲述了python计算牛顿迭代多项式的方法.分享给大家供大家参考.具体实现方法如下: ''' p = evalPoly(a,xData,x). Evaluates Newton's polyn ...

  6. c语言静态变量存在堆还是栈,c 类 static 函数 什么样是静态变量?嵌入式C语言的堆栈管理如何实现...

    C语言中静态变量是什么意思,有什么作用,static在数据类型前面表示什么 最近刚看了C存储类的章节.所以来说说. C语言为变量提供了⑤种不同的存储模型,或者说是存储类. ①个变量可以用存储时期描述, ...

  7. python自定义一个可迭代类_python进阶之自定义可迭代的类

    自定义可迭代的类 列表可以获取列表的长度,然后使用变量i对列表索引进行循环,也可以获取集合的所有元素,且容易理解.没错,使用列表的代码是容易理解,也很好操作,但这是要付出代价的.列表之所以可以用索引来 ...

  8. matlab迭代算法实例_【优化求解】基于NSGA2的求解多目标柔性车间调度算法

    柔性作业车间调度问题(FJSP)是经典作业车间调度问题的重要扩展,其中每个操作可以在多台机器上处理,反之亦然.结合实际生产过程中加工时间.机器负载.运行成本等情况,建立了多目标调度模型.针对NSGA2 ...

  9. python : 自定义可迭代类,__iter__ ,__next__的作用

    在python3中,如果定义了一个类,类中有 __iter__(self),__next__(self)两个函数,则该类具有了迭代器的属性, 具体性质体现在: 如果该类的对象出现在一个for循环中,那 ...

最新文章

  1. acme.sh及https证书实践
  2. boost::replace_copy_if相关的测试程序
  3. pat 乙级 1056 组合数的和(C++)
  4. Mybatis-plus批量插入、批量修改数据saveBatch等速度缓慢
  5. MTCNN可攻破?华为提出一种可复制、可靠的攻击方法
  6. [**收藏**] Warcraft 小游戏 -- Tower Defense(1) -- Flash Element TD
  7. 微服务get请求条用_SpringCloud微服务(07):Zipkin组件,实现请求链路追踪
  8. 来学习一下概率论基本知识,它能让防止你的模型过拟合
  9. np-hard证明实例 规约
  10. 实型变量(浮点型变量)、字符型数据、字符串常量(变量)、字符常量(变量)
  11. Go语言使用RabbitMQ
  12. [RK3399][Android7.1] 调试笔记 --- DDR动态调节功能开启
  13. fashion-mnist简介和使用及下载
  14. 前后端分离vue2+node易购商城后台管理系统
  15. ARM处理器的 Handler mode 与 Thread mode 以及 MSP 和 PSP
  16. 可以背在身后的机器人助你一臂之力
  17. Mongdb重启后dirty很高,cache打满
  18. AI 建模师 素养手冊(2)
  19. STM32关于PVD低电压能检测的知识
  20. IDEA部署Tomcat时出现的 Brower Error 问题

热门文章

  1. bzoj3482,jzoj3238-超时空旅行hiperprostor【最短路,凸包,斜率优化】
  2. P5022-旅行【基环树,dfs】
  3. 【动态规划】叠放箱子问题(ssl 1640)
  4. 2017 ACM Jordanian Collegiate J.Efficiency Test 动态规划、类倍增
  5. codeforces gym-101755 D-Transfer Window 二分图匹配、递归
  6. vue-cli2、vue-cli3脚手架详细讲解
  7. Oracle入门(十四.18)之使用动态SQL
  8. Java Web应用的代码分层最佳实践
  9. sql事务、视图和索引
  10. (归并排序 快排 堆)