欲立则先破,想要弄清楚 hprof 是如何记录咱们的类,对象所占内存大小,以及引用链等,则先要破解出 hprof 的文件协议及内容。即 hprof 到底写入了什么?dom
同其余类型文件,如Bitmap,Hprof 也有本身的文件的协议。其包括了 hprof head 和 hprof body 两部分。head 部分主要包括版本,索引等,而 body 则存储了类,对象,stack trace 等。貌似又是一个套路。spa
主要写入了文件的版本描述及版本号,当前为固定的以'/0'结尾的字串,ID标识的size,写入时间戳。对象
19 | 4 | 8 |
"JAVA PROFILE 1.0.3/0" | indentify size | time stamp |
写入了当前所用到全部的字符串,包括类名,常量等。这里的TAG,以及其余数据的 TAG 很重要。解析 Hprof 文件协议时,就是用 TAG 来区分的索引
string 的 TAG 定义为 0x01ip
1 | 4 | 4 | 4 | strlen(string content) |
tag | time stamp | length | id | string content |
tag | time stamp | length | id | string content |
tag | time stamp | length | id | string content |
...... | ...... | ...... | ..... | ...... |
class 的 TAG 定义为 0x02内存
1 | 4 | 4 | 4 | 4 | 4 | 4 |
tag | time stamp | length | class serial number | class object id | stack trace serial number | class name string idci |
...... | ..... | ..... | ..... | ...... | ...... | ...... |
stack trace 的 TAG 定义为 0x05。实际取出来是空,即 num frames 为 0element
1 | 4 | 4 | 4 | 4 | 4 |
tag | time stamp | length | serial number | thread serial number | num frames |
Body 部分,以一行为划分,每一行包括全部的 heap dump类型,以及该 heap dump下的子类型rem
1 | 4 | 4 |
tag(0x0C,HEAP_DUMP) | time stamp | length |
tag(0x1C,HEAP_DUMP_SEGMENT) | time stamp | length |
下面的 string id 对应的 string table 中的 string id字符串
1 | 4 | ||||
0xff(ROOT_UNKNOWN) | string id | [RootType.UNKNOW] | |||
1 | 4 | 4 | |||
0x01(ROOT_JNI_GLOBAL) | string id | jni global id | [RootType.NATIVE_STATIC] | ||
1 | 4 | 4 | 4 | ||
0x02(ROOT_JNI_LOCAL) | string id | thread serial number | stack frame number | ||
1 | 4 | 4 | 4 | ||
0x03(ROOT_JAVA_FRAME) | string id | thread serial number | stack frame number | ||
1 | 4 | 4 | |||
0x04(ROOT_NATIVE_STACK) | string id | thread serial number | |||
1 | 4 | ||||
0x05(ROOT_STICKY_CLASS) | string id | [RootType.SYSTEM_CLASS] | |||
1 | 4 | 4 | |||
0x06(ROOT_THREAD_BLOCK) | string id | thread serial number | |||
1 | 4 | ||||
0x07(ROOT_MONITOR_USED) | string id | [RootType.BUSY_MONITOR] | |||
1 | 4 | 4 | 4 | ||
0x08(ROOT_THREAD_OBJECT) | string id | thread serial number | stack frame number | ||
1 | 4 | ||||
0x20(ROOT_CLASS_DUMP) | string id | 有点复杂,在下面单独列出来 | |||
1 | 4 | 4 | 4 | 4 | |
0x21(ROOT_INSTANCE_DUMP) | string id | stack id | class id | remaining(skip) | |
1 | 4 | 4 | 4 | 4 | |
0x22(ROOT_OBJECT_ARRAY_DUMP) | string id | stack id | num elements | class id | 4 * num elements(skip) |
1 | 4 | 4 | 4 | 1 | |
0x23(ROOT_PRIMITIVE_ARRAY_DUMP) | string id | stack id | num elements | primitive type | 4 * num elements(skip) |
1 | |||||
0xc3(ROOT_PRIMITIVE_ARRAY_NODATA) | 暂时表示错误类型 | ||||
1 | 4 | 4 | |||
0xfe(ROOT_HEAP_DUMP_INFO) | heap id | heap name id(string id) | |||
1 | 4 | ||||
0x89(ROOT_INTERNED_STRING) | string id | [RootType.INTERNED_STRING] | |||
1 | 4 | ||||
0x8a(ROOT_FINALIZING) | string id | [RootType.FINALIZING] | |||
1 | 4 | ||||
0x8b(ROOT_DEBUGGER) | string id | [RootType.DEBUGGER] | |||
1 | 4 | ||||
0x8c(ROOT_REFERENCE_CLEANUP) | string id | [RootType.REFERENCE_CLEANUP] | |||
1 | 4 | ||||
0x8d(ROOT_VM_INTERNAL) | string id | [RootType.VM_INTERNAL] | |||
1 | 4 | 4 | 4 | ||
0x8e(ROOT_JNI_MONITOR) | string id | thread serial number | stack frame number | ||
1 | 4 | ||||
0x90(ROOT_UNREACHABLE) | string id | [RootType.UNREACHABLE] |
0x20(ROOT_CLASS_DUMP) | 1 |
id | 4 |
stack serial number | 4 |
super class id | 4 |
class loader id | 4 |
signeres id | 4 |
protection domain id | 4 |
reserved | 4 |
reserved | 4 |
instance size | 4 |
const pool num entries | 2 |
2 * num entries | |
static fields num entries | 2 |
static fields | static fields num entries * (static fields),下面会再单独列出来 |
instance fields num entries | 2 |
instance fields | instance fields num entries * (instance fields) |
4 | 1 | 4 |
static fields id | static fields type | type size |
4 | 1 |
instance id | instance type |