JPDA 架构研究13 - Agent利用环境指针访问VM(类管理篇)

引入:

上文中提到Agent如何利用环境指针访问VM的(Watch)功能,这里主要讲解如何去管理类的。

分类9:管理类

a.GetLoadedClasses. 获得虚拟机中所有被加载的类的数组。

jvmtiError
GetLoadedClasses(jvmtiEnv* env,
            jint* class_count_ptr,
              jclass** classes_ptr)

从返回值可以看出,class_count_ptr表示被加载的类的数量,classes_ptr表示类的数组列表。

注意,这里不包含内置类型对应的包装器类。比如说java.lang.Integer.TYPE就不包含在此列表中。

b.GetClassLoaderClasses.获取虚拟机中所有的Classloader所管理的类。

jvmtiError
GetClassLoaderClasses(jvmtiEnv* env,
            jobject initiating_loader,
            jint* class_count_ptr,
            jclass** classes_ptr)

c.GetClassSignature.获取某类的签名

jvmtiError
GetClassSignature(jvmtiEnv* env,
            jclass klass,
            char** signature_ptr,
            char** generic_ptr)

这里的签名是用的JNI类型签名方式,比如说java.util.List 的类签名是 "Ljava/util/List;" 
int[] 的类签名是 "[I"

d.GetClassStatus.获取类状态

jvmtiError
GetClassStatus(jvmtiEnv* env,
            jclass klass,
            jint* status_ptr)

类的有如下状态,分别用状态标志位来表示:

JVMTI_CLASS_STATUS_VERIFIED 1 类的字节码已经被修改
JVMTI_CLASS_STATUS_PREPARED 2 类准备状态已经完成 
JVMTI_CLASS_STATUS_INITIALIZED 4 类初始化完毕,静态初始化块已运行 S
JVMTI_CLASS_STATUS_ERROR 8 类初始化错误,因此不可使用。 
JVMTI_CLASS_STATUS_ARRAY 16 类是个数组
JVMTI_CLASS_STATUS_PRIMITIVE 32  类是个原子类(比如 java.lang.Integer.TYPE).  
 

e.GetSourceFileName.获取指定类的源代码文件名

jvmtiError
GetSourceFileName(jvmtiEnv* env,
            jclass klass,
            char** source_name_ptr)

f.GetClassModifiers.获取类的访问修饰符

jvmtiError
GetClassModifiers(jvmtiEnv* env,
            jclass klass,
            jint* modifiers_ptr)

一般类的访问修饰符就是 public/private/protected ,另外还有final.

另外,如果类是原子类(比如java.lang.Integer.TYPE),则它的访问修饰符必定是public final.并且一定没有对应的interface.

g.GetClassMethods.获取类的方法列表。

jvmtiError
GetClassMethods(jvmtiEnv* env,
            jclass klass,
            jint* method_count_ptr,
            jmethodID** methods_ptr)

按照约定,分别返回方法数量以及方法的列表。注意,这个方法列表还包括构造器和静态初始块。

h.GetClassFields.获取类的字段列表。

jvmtiError
GetClassFields(jvmtiEnv* env,
            jclass klass,
            jint* field_count_ptr,
            jfieldID** fields_ptr)

注意,这个字段列表只包含直接声明的字段,不包含它从父类中继承过来的字段。字段的返回顺序精确的等同于在类文件中声明的顺序。

i.GetImplementedInterfaces.获取类所实现的接口

jvmtiError
GetImplementedInterfaces(jvmtiEnv* env,
            jclass klass,
            jint* interface_count_ptr,
            jclass** interfaces_ptr)

注意,对于类来说,这里只返回它直接implements XXX,XXX的接口。

对于接口来说,这里返回它 extends XXX的接口。

j.IsInterface.判断某类是否是一个接口

jvmtiError
IsInterface(jvmtiEnv* env,
            jclass klass,
            jboolean* is_interface_ptr)

k.IsArrayClass.判断某类是否是一个数组类

jvmtiError
IsArrayClass(jvmtiEnv* env,
            jclass klass,
            jboolean* is_array_class_ptr)

l.GetClassLoader.获取某类对应的类加载器的引用。

jvmtiError
GetClassLoader(jvmtiEnv* env,
            jclass klass,
            jobject* classloader_ptr)

m.GetSourceDebugExtension.获取类的debug扩展信息。

jvmtiError
GetSourceDebugExtension(jvmtiEnv* env,
            jclass klass,
            char** source_debug_extension_ptr)

n.RedefineClasses.重新定义一组类 (强大的热交换技术)

typedef struct {
    jclass klass;
    jint class_byte_count;
    const unsigned char* class_bytes;
} jvmtiClassDefinition;
jvmtiError
RedefineClasses(jvmtiEnv* env,
            jint class_count,
            const jvmtiClassDefinition* class_definitions)

这功能挺有趣,因为如果指定的字节码,则重新定义某类。所以该方法通过传入一组字节码来重新定义一组类。重新定义某类之后,对于该类会有如下一些改变:

(1).线程无需被挂起。

(2).类中的所有断点都被清除。

(3).所有属性都被更新。(注意,这里的属性是class对象的属性,不是类文件中的属性,那个叫字段field)

(4).类的已有的所有实例,其在堆上的ID都不受影响,其含有的字段值都不受影响。

另外,对于类的重新定义,也有些约束:

(1)重定义可以改变方法体,常量池,字段。

(2)重定义不可以添加/修改/删除任何类中的方法和字段。

(3)重定义不可以修改方法签名,改变访问修饰符和继承关系。

时间: 2024-12-09 16:26:16

JPDA 架构研究13 - Agent利用环境指针访问VM(类管理篇)的相关文章

JPDA 架构研究9 - Agent利用环境指针访问VM (堆管理篇)

引入: 上文中我们讨论了Agent利用环境指针访问VM的堆栈操作部分,这里我们讨论堆操作部分. 分类5:堆操作 a. GetTag. 获取对象在堆上的唯一标识符(长整型). jvmtiError GetTag(jvmtiEnv* env,             jobject object,             jlong* tag_ptr) b.SetTag.为对象设置堆上的唯一标示符. jvmtiError SetTag(jvmtiEnv* env,             jobje

JPDA 架构研究10 - Agent利用环境指针访问VM(局部变量管理篇)

引入: 上篇我们讲解了Agent如何利用环境指针访问VM的管理堆的操作.这里主要讲解如何管理局部变量. 分类6:局部变量管理 a.GetLocalObject. 获取局部对象 jvmtiError GetLocalObject(jvmtiEnv* env,             jthread thread,             jint depth,             jint slot,             jobject* value_ptr) b.GetLocalInt.

JPDA 架构研究14 - Agent利用环境指针访问VM(对象管理篇)

引入: 上文讨论了Agent如何利用环境指针访问VM(管理类),这里讨论如何管理对象. 分类9:对象管理 a.GetObjectSize.  获取对象的大小, jvmtiError GetObjectSize(jvmtiEnv* env,             jobject object,             jlong* size_ptr) 注意,这不是真实大小,这只是从对象的定义代码中获得的一个预估值. b.GetObjectHashCode.获取对象的hash值. jvmtiErr

JPDA 架构研究5 - Agent利用环境指针访问VM (内存管理篇)

引入: 我们在前面说到JVMTI的客户端Agent,又提到Agent通过环境指针来访问VM.这里就来看看环境指针到底有多大的访问VM的能力. 分类1:内存管理 a.Allocate. 分配内存 jvmtiError Allocate(jvmtiEnv* env,             jlong size,             unsigned char** mem_ptr) size:分配的字节数. mem_ptr:分配内存的首地址. b.Deallocate.释放内存,释放已经分配的内

JPDA 架构研究7 - Agent利用环境指针访问VM(线程组管理篇)

引入: 上篇文章中我们讨论了Agent利用环境指针访问VM的线程操作,这里讨论线程组操作. 分类3:线程组操作 a.GetTopThreadGroups.让Agent获取VM中的所有全局的线程组. jvmtiError GetTopThreadGroups(jvmtiEnv* env,             jint* group_count_ptr,             jthreadGroup** groups_ptr) 函数会返回全局的线程组的数量和线程组的列表. b.GetThre

JPDA 架构研究8 - Agent利用环境指针访问VM(堆栈管理篇)

引入: 上篇文章讲解了Agent利用环境指针访问VM的线程组操作,这里讨论下堆栈操作. 分类4:堆栈操作 a. GetStackTrace.获取某线程的堆栈. jvmtiError GetStackTrace(jvmtiEnv* env,             jthread thread,             jint start_depth,             jint max_frame_count,             jvmtiFrameInfo* frame_buff

JPDA 架构研究11 - Agent利用环境指针访问VM(断点操作篇)

引入: 上文我们讲解了Agent如何利用环境指针访问VM(操作局部变量篇),这里主要关注于如何操作断点. 这个在调试期间非常频繁的被使用. 分类7:断点操作 a.SetBreakPoint.设置断点,它会产生一个BreakPoint事件. jvmtiError SetBreakpoint(jvmtiEnv* env,             jmethodID method,             jlocation location) 设置断点指令.从这里可以看出需要传两个参数,一个是被设断

JPDA 架构研究12 - Agent利用环境指针访问VM(观察字段篇)

引入: 上文我们讲解了Agent如何利用环境指针访问VM的操作断点部分,这里我们详细讲解观察字段篇.这也是我们调试期间用的最多的操作.因为我们要不断从Watch Field看他们值的改变. 分类8:观察字段 a.SetFieldAccessWatch.启用对某字段进行访问的观察.每次访问都会创建一个FieldWatch事件. jvmtiError SetFieldAccessWatch(jvmtiEnv* env,             jclass klass,             jf

JPDA 架构研究15 - Agent利用环境指针访问VM(字段访问篇)

引入: 上文中我们已经探讨了Agent如何利用环境指针访问VM(对象级的操作),现在我们 讨论让Agent利用环境指针访问类的字段. 分类10:字段访问 a.GetFieldName.获取某类的某字段名字 jvmtiError GetFieldName(jvmtiEnv* env,             jclass klass,             jfieldID field,             char** name_ptr,             char** signat