我們來看下上一節中的顯示效果和本節例子的最終顯示效果的異同:
上圖是上一節中例子中的顯示效果。
上圖是本節例子中的最終顯示效果。可以看到,我們不僅可以獲知某個class的執行個體數量,執行個體的總占用空間,以及class name。還能觀察到class及其整棵引用樹上的class的執行個體數量,空間,name等。
<a></a>
本節沒有用到任何新的jvmti函數。不過比起第二節,會基于更多函數的入參(上一節中,許多入參都是閑置)來實作本例子中的功能。
上一節中,我們了解到,通過followreference的jvmtiheapreferencecallback可以得到jvmti函數從heap root開始掃描其下存活對象的引用關系樹,這個過程非常類似gc。
為了實作本例的功能,我們需要利用這個函數,記錄它回報的引用關系,并加工就能得到一顆完整的引用關系樹。
回調函數中,我們需要關注的入參是 class_tag和referer_class_tag,後者代表引用目前對象的對象所屬class的标簽。通過這個标簽,我們可以從ci_map中擷取真實的classinfo. 具體實作詳見下文的代碼片段。 本例的難點是如何記錄和構造這顆引用關系樹。 筆者的實作方式比較簡單,在classinfo結構中,維護一個單向連結清單。
在記錄引用關系的同時,我們還需要注意:
1,遞歸引用需要排除。比如 a類成員變量也是a類自身。
2,java.lang.class不需要計算到引用關系中。我們知道任何一個class都有引用它。忽略它可以提高存儲和計算效率。
3,節點的祖先不應該出現重複的節點。
比如 a->b->c->a,這裡第四層的a就不應該出現在引用關系樹上。否則會重複列印(a後面又是 b->c->a->b->c->a…)。
實作代碼片段:
這份源碼與上一節中的源碼非常類似,主要是 heapfrcallback 函數裡添加了引用關系的記錄。列印結果的程式片段處,添加遞歸列印樹的函數。此外就是添加了新的資料結構 referrer 。
下面是完整的源碼,copy,另存為,編譯即可運作。
jvmti tutorial - jmap -histo include reference.
*
created on: 2011-3-3
author: kenwu
*/
#include
using namespace std;
class referrer {
public:
referrer() {
cls_id = 0;
next = null;
}
~referrer() {
delete next;
int cls_id;
referrer *next;
};
class classinfo {
classinfo() {
name = null;
instance_cnt = 0;
instance_size = 0;
cls_obj_flag = 0;
referrer = null;
~classinfo() {
free(name);
delete referrer;
char name;
int instance_cnt;
long instance_size;
int cls_obj_flag;
referrer referrer;
classinfo *ci_map;
jvmtienv jvmti;
int seq;
int total_cls_size;
int depth = 5;
/**
解析class符号,抽取出class name并格式化。
@return class name
/
char getclassname(jclass cls) {
int xl = 0;
char sig;
char data;
jvmti->getclasssignature(cls, &sig, null);
if (sig) {
return data;
jint jnicall heapfrcallback(jvmtiheapreferencekind reference_kind,
const jvmtiheapreferenceinfo reference_info, jlong class_tag,
jlong referrer_class_tag, jlong size, jlong tag_ptr,
jlong referrer_tag_ptr, jint length, void user_data) {
// clean duplicate
int act_obj = 0;
if (tag_ptr == 0) { tag_ptr = ++seq;
act_obj = 1;
} else if (*tag_ptr cls_obj_flag == 0) {
ci->cls_obj_flag = 1;
jint jnicall untagcallback(jlong class_tag, jlong size, jlong tag_ptr,
jint length, void user_data) {
*tag_ptr = 0;
return jvmti_visit_objects;
referrer get_max(referrer head) {
referrer *max = head;
while (null != head) {
if (ci_map[head->cls_id]->instance_size
> ci_map[max->cls_id]->instance_size) {
max = head;
head = head->next;
return max;
void sort_referrers(referrer head) {
referrer node = head, *tmp;
int value;
while (null != node) {
tmp = get_max(node);
if (node != tmp) {
value = node->cls_id;
node->cls_id = tmp->cls_id;
tmp->cls_id = value;
node = node->next;
bool allontree(referrer *ci, set ref_tree) {
while (null != ci) {
if (ref_tree.find(ci->cls_id) == ref_tree.end()) {
return false;
ci = ci->next;
return true;
void printrefinfo(classinfo ci, int level, set ref_tree,
set grade_format) {
if (++level >= depth)
return;
ref_tree.insert(ci->cls_id);
referrer referrer = ci->referrer;
sort_referrers(referrer);
int max = 0;
while (referrer != null) {
classinfo *c1 = ci_map[referrer->cls_id];
if (max++ cls_id) == ref_tree.end()) {
string strbuf(“”);
jniexport jint jnicall agent_onattach(javavm jvm, char options,
void reserved) {
/*
jniexport void jnicall agent_onunload(javavm *vm) {
// nothing to do
本文來源于"阿裡中間件團隊播客",原文發表時間" 2011-03-17 "