一.前言
???????在平常的Android應(yīng)用開發(fā)中获高,經(jīng)常會(huì)遇到應(yīng)用因內(nèi)存問題導(dǎo)致的異常,可能大家第一反應(yīng)是:分析log及堆棧信息;但是我們知道堆棧信息只是最后的結(jié)果表現(xiàn)而已饮潦,真正出問題的地方或原因是之前由于不正常的內(nèi)存操作扼雏,導(dǎo)致內(nèi)存一直占用沒有被釋放坚嗜,出現(xiàn)內(nèi)存泄露,最后OOM诗充。
???????為了解決上述問題苍蔬,最直接有效的方式是:動(dòng)態(tài)內(nèi)存分配監(jiān)聽
???????記錄程序執(zhí)行過程中的動(dòng)態(tài)內(nèi)存分配,當(dāng)發(fā)生OOM時(shí)蝴蜓,就能夠分析記錄信息掌握內(nèi)存使用情況碟绑;如:是否存在內(nèi)存泄露俺猿、內(nèi)存抖動(dòng)等問題。
???????接下來(lái)就是主角登場(chǎng)了----JVMTI格仲。
二.JVMTI
a.簡(jiǎn)介
???????Java虛擬機(jī)工具接口押袍,是Java虛擬機(jī)提供的一整套后門,通過這套后門可以對(duì)虛擬機(jī)方方面面進(jìn)行監(jiān)控凯肋,它可以監(jiān)控jvm內(nèi)部事件的執(zhí)行伯病,包括內(nèi)存申請(qǐng)、線程創(chuàng)建否过、類加載午笛、GC信息、方法執(zhí)行等苗桂,也可以控制JVM的某些行為药磺。具體可以參考o(jì)racle 的文檔:JVM Tool Interface
???????JVMTI 本質(zhì)上是在JVM內(nèi)部的許多事件進(jìn)行了埋點(diǎn),通過這些埋點(diǎn)可以給外部提供當(dāng)前上下文的一些信息煤伟,甚至可以接受外部的命令來(lái)改變下一步的動(dòng)作癌佩。
???????外部程序一般利用C/C++實(shí)現(xiàn)一個(gè)JVMTI Agent,JVMTI Agent的啟動(dòng)需要虛擬機(jī)的支持便锨,在Agent里面注冊(cè)一些JVM事件的回調(diào)围辙。當(dāng)事件發(fā)生時(shí)JVMTI調(diào)用這些回調(diào)方法。Agent可以在回調(diào)方法里面實(shí)現(xiàn)自己的邏輯放案。
???????JVMTI Agent是以動(dòng)態(tài)鏈接庫(kù)的形式被虛擬機(jī)加載的姚建, Agent和虛擬機(jī)運(yùn)行在同一個(gè)進(jìn)程中,虛擬機(jī)通過dlopen打開Agent動(dòng)態(tài)鏈接庫(kù)吱殉。
b.功能
???????一些重要的功能包括:
??????????????1.重新定義類
??????????????2.跟蹤對(duì)象分配和垃圾回收過程
??????????????3.遵循對(duì)象的引用樹掸冤,遍歷堆中的所有對(duì)象
??????????????4.檢查 Java 調(diào)用堆棧
??????????????5.暫停(和恢復(fù))所有線程
???????不同版本的 Android 可能會(huì)提供不同的功能
c.兼容性
???????此功能需要僅針對(duì) Android 8.0 及更高版本提供的核心運(yùn)行時(shí)支持,設(shè)備制造商無(wú)需進(jìn)行任何更改即可實(shí)現(xiàn)此功能友雳,它是 AOSP 的一部分稿湿。
???????從 Android 8.0 開始,Android ART已經(jīng)加入了JVMTI的相關(guān)功能押赊。目錄位于art/runtime/openjdkjvmti下饺藤,從Android.bp可以看到,編譯會(huì)生成libopenjdkjvmtid.so流礁、libopenjdkjvmti.so文件涕俗,其中核心文件是jvmti.h文件,里面定義了一些核心方法和結(jié)構(gòu)體崇棠。本地實(shí)現(xiàn)時(shí)咽袜,需要引入該文件來(lái)實(shí)現(xiàn)對(duì)應(yīng)的Capabilities。
d.API調(diào)用
???????在 Android 9.0枕稀,已將API添加到framework/base/core/java/android/os/Debug.java中询刹,對(duì)應(yīng)API如下:
/**
* Attach a library as a jvmti agent to the current runtime, with the given classloader
* determining the library search path.
* <p>
* Note: agents may only be attached to debuggable apps. Otherwise, this function will
* throw a SecurityException.
*
* @param library the library containing the agent.
* @param options the options passed to the agent.
* @param classLoader the classloader determining the library search path.
*
* @throws IOException if the agent could not be attached.
* @throws SecurityException if the app is not debuggable.
*/
public static void attachJvmtiAgent(@NonNull String library, @Nullable String options,
@Nullable ClassLoader classLoader) throws IOException {
Preconditions.checkNotNull(library);
Preconditions.checkArgument(!library.contains("="));
if (options == null) {
VMDebug.attachAgent(library, classLoader);
} else {
VMDebug.attachAgent(library + "=" + options, classLoader);
}
}
???????接下來(lái)一起分析一下當(dāng)app調(diào)用完attachJvmtiAgent()后谜嫉,源碼的執(zhí)行流程,本文以Android 8.1作為源碼進(jìn)行分析凹联。
三.源碼分析
???????通過上面的分析可以看到沐兰,在attachJvmtiAgent()內(nèi)部,會(huì)調(diào)用VMDebug類內(nèi)部的attachAgent()方法蔽挠,由于attachJvmtiAgent()是在Android 9.0才加入的住闯,那么在Android 8.1平臺(tái)只能通過反射來(lái)執(zhí)行,直接反射VMDebug的attachAgent()方法澳淑。
a.VMDebug.java
???????該類路徑:libcore/dalvik/src/main/java/dalvik/system/VMDebug.java
/**
* Attaches an agent to the VM.
*
* @param agent The path to the agent .so file plus optional agent arguments.
*/
public static native void attachAgent(String agent) throws IOException;
???????從上面可以看到比原,該類是native方法,會(huì)通過Jni調(diào)用到native層對(duì)應(yīng)的實(shí)現(xiàn)方法杠巡,該實(shí)現(xiàn)在dalvik_system_VMDebug.cc內(nèi)部量窘。
b.dalvik_system_VMDebug.cc
???????該類路徑:art/runtime/native/dalvik_system_VMDebug.cc
static void VMDebug_attachAgent(JNIEnv* env, jclass, jstring agent) {
if (agent == nullptr) {
ScopedObjectAccess soa(env);
ThrowNullPointerException("agent is null");
return;
}
if (!Dbg::IsJdwpAllowed()) {
ScopedObjectAccess soa(env);
ThrowSecurityException("Can't attach agent, process is not debuggable.");
return;
}
std::string filename;
{
ScopedUtfChars chars(env, agent);
if (env->ExceptionCheck()) {
return;
}
filename = chars.c_str();
}
Runtime::Current()->AttachAgent(filename);
}
???????在VMDebug_attachAgent()內(nèi)部首先判斷傳入so的路徑是否為空,然后判斷是否為debug模式氢拥,以上兩個(gè)條件都滿足[注意jvmti只適應(yīng)于debug版本]蚌铜,最后會(huì)調(diào)用AttachAgent()方法,該方法實(shí)現(xiàn)是在runtime.cc內(nèi)部嫩海。
c.runtime.cc
???????該類路徑:art/runtime/runtime.cc
// Attach a new agent and add it to the list of runtime agents
//
// TODO: once we decide on the threading model for agents,
// revisit this and make sure we're doing this on the right thread
// (and we synchronize access to any shared data structures like "agents_")
//
void Runtime::AttachAgent(const std::string& agent_arg) {
std::string error_msg;
if (!EnsureJvmtiPlugin(this, &plugins_, &error_msg)) {
LOG(WARNING) << "Could not load plugin: " << error_msg;
ScopedObjectAccess soa(Thread::Current());
ThrowIOException("%s", error_msg.c_str());
return;
}
ti::Agent agent(agent_arg);
int res = 0;
ti::Agent::LoadError result = agent.Attach(&res, &error_msg);
if (result == ti::Agent::kNoError) {
agents_.push_back(std::move(agent));
} else {
LOG(WARNING) << "Agent attach failed (result=" << result << ") : " << error_msg;
ScopedObjectAccess soa(Thread::Current());
ThrowIOException("%s", error_msg.c_str());
}
}
???????在AttachAgent()內(nèi)部冬殃,會(huì)根據(jù)傳入?yún)?shù)來(lái)創(chuàng)建Agent,然后執(zhí)行Attach()方法叁怪,該方法是在agent.h內(nèi)部审葬。
d.agent.h
???????該類路徑:art/runtime/ti/agent.h
LoadError Attach(/*out*/jint* call_res, /*out*/std::string* error_msg) {
VLOG(agents) << "Attaching agent: " << name_ << " " << args_;
return DoLoadHelper(true, call_res, error_msg);
}
bool IsStarted() const {
return dlopen_handle_ != nullptr;
}
???????在Attach()內(nèi)部會(huì)調(diào)用DoLoadHelper(),該方法位于agent.cc內(nèi)部骂束。
e.agent.cc
???????該類路徑:art/runtime/ti/agent.cc
Agent::LoadError Agent::DoLoadHelper(bool attaching,
/*out*/jint* call_res,
/*out*/std::string* error_msg) {
......
//如果打開過耳璧,就不會(huì)再打開了,IsStarted()方法在agent.h方法內(nèi)部判斷
if (IsStarted()) {
*error_msg = StringPrintf("the agent at %s has already been started!", name_.c_str());
VLOG(agents) << "err: " << *error_msg;
return kAlreadyStarted;
}
//調(diào)用DoDlOpen()
LoadError err = DoDlOpen(error_msg);
if (err != kNoError) {
VLOG(agents) << "err: " << *error_msg;
return err;
}
AgentOnLoadFunction callback = attaching ? onattach_ : onload_;
if (callback == nullptr) {
*error_msg = StringPrintf("Unable to start agent %s: No %s callback found",
(attaching ? "attach" : "load"),
name_.c_str());
VLOG(agents) << "err: " << *error_msg;
return kLoadingError;
}
// Need to let the function fiddle with the array.
std::unique_ptr<char[]> copied_args(new char[args_.size() + 1]);
strlcpy(copied_args.get(), args_.c_str(), args_.size() + 1);
//回調(diào)加載的本地庫(kù)內(nèi)部的Agent_OnAttach()方法
*call_res = callback(Runtime::Current()->GetJavaVM(),
copied_args.get(),
nullptr);
if (*call_res != 0) {
*error_msg = StringPrintf("Initialization of %s returned non-zero value of %d",
name_.c_str(), *call_res);
VLOG(agents) << "err: " << *error_msg;
return kInitializationError;
} else {
return kNoError;
}
}
???????內(nèi)部調(diào)用方法DoDlOpen():
Agent::LoadError Agent::DoDlOpen(/*out*/std::string* error_msg) {
DCHECK(error_msg != nullptr);
DCHECK(dlopen_handle_ == nullptr);
DCHECK(onload_ == nullptr);
DCHECK(onattach_ == nullptr);
DCHECK(onunload_ == nullptr);
//調(diào)用dlopen()
dlopen_handle_ = dlopen(name_.c_str(), RTLD_LAZY);
if (dlopen_handle_ == nullptr) {
*error_msg = StringPrintf("Unable to dlopen %s: %s", name_.c_str(), dlerror());
return kLoadingError;
}
//通過FindSymbol來(lái)從加載的庫(kù)中尋找Agent_xx方法
onload_ = reinterpret_cast<AgentOnLoadFunction>(FindSymbol(AGENT_ON_LOAD_FUNCTION_NAME));
if (onload_ == nullptr) {
VLOG(agents) << "Unable to find 'Agent_OnLoad' symbol in " << this;
}
onattach_ = reinterpret_cast<AgentOnLoadFunction>(FindSymbol(AGENT_ON_ATTACH_FUNCTION_NAME));
if (onattach_ == nullptr) {
VLOG(agents) << "Unable to find 'Agent_OnAttach' symbol in " << this;
}
onunload_= reinterpret_cast<AgentOnUnloadFunction>(FindSymbol(AGENT_ON_UNLOAD_FUNCTION_NAME));
if (onunload_ == nullptr) {
VLOG(agents) << "Unable to find 'Agent_OnUnload' symbol in " << this;
}
return kNoError;
}
???????內(nèi)部調(diào)用方法FindSymbol():
void* Agent::FindSymbol(const std::string& name) const {
CHECK(IsStarted()) << "Cannot find symbols in an unloaded agent library " << this;
return dlsym(dlopen_handle_, name.c_str());
}
???????通過以上調(diào)用關(guān)系可以看到展箱,當(dāng)我們加載完本地so后,然后調(diào)用Debug.attachJvmtiAgent()[Android 9.0]或反射調(diào)用VMDebug.attachAgent()[Android 8.1]蹬昌,會(huì)回調(diào)so內(nèi)部的Agent_XX方法混驰,本地測(cè)試發(fā)現(xiàn),會(huì)回調(diào)Agent_OnAttach()方法皂贩,那我們就在Agent_OnAttach()內(nèi)部來(lái)初始化Jvmti的工作栖榨。
???????總結(jié)一下調(diào)用流程:
四.案例分析
???????本案例實(shí)現(xiàn)了對(duì)應(yīng)用內(nèi)部對(duì)象創(chuàng)建及釋放、方法進(jìn)入及退出事件的監(jiān)聽明刷。
???????由于需要將so作為agent進(jìn)行attach婴栽,所以涉及到j(luò)ni編程,生成so辈末,關(guān)于jni編程愚争,可以參考之前的一篇文章AndroidStudio 來(lái)編寫jni及生成so映皆。本文就略過了,直接上代碼轰枝。
a.Monitor.java
public class Monitor {
private static final String LIB_NAME = "monitor_agent";
public static void init(Context application) {
//最低支持Android 8.0
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return;
}
//獲取so的地址后加載
String agentPath = getAgentLibPath(application);
System.load(agentPath);
//加載jvmti
attachAgent(agentPath, application.getClassLoader());
//開啟jvmti事件監(jiān)聽
agent_init(root.getAbsolutePath());
}
private static void attachAgent(String agentPath, ClassLoader classLoader) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
Debug.attachJvmtiAgent(agentPath, null, classLoader);
} else {
Class vmDebugClazz = Class.forName("dalvik.system.VMDebug");
Method attachAgentMethod = vmDebugClazz.getMethod("attachAgent", String.class);
attachAgentMethod.setAccessible(true);
attachAgentMethod.invoke(null, agentPath);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
private static String getAgentLibPath(Context context) {
try {
ClassLoader classLoader = context.getClassLoader();
Method findLibrary = ClassLoader.class.getDeclaredMethod("findLibrary", String.class);
//so的地址
String jvmtiAgentLibPath = (String) findLibrary.invoke(classLoader, LIB_NAME);
//將so拷貝到程序私有目錄 /data/data/packageName/files/monitor/agent.so
File filesDir = context.getFilesDir();
File jvmtilibDir = new File(filesDir, "monitor");
if (!jvmtilibDir.exists()) {
jvmtilibDir.mkdirs();
}
File agentLibSo = new File(jvmtilibDir, "agent.so");
if (agentLibSo.exists()) {
agentLibSo.delete();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Files.copy(Paths.get(new File(jvmtiAgentLibPath).getAbsolutePath()), Paths.get((agentLibSo).getAbsolutePath()));
}
return agentLibSo.getAbsolutePath();
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public static void release() {
agent_release();
}
private native static void agent_init();
private native static void agent_release();
}
???????1.application內(nèi)部通過init()來(lái)進(jìn)行初始化捅彻,包括加載庫(kù)(后面補(bǔ)充一下so加載流程)、創(chuàng)建存放日志的目錄鞍陨;
???????2.通過getAgentLibPath()來(lái)獲取到so的路徑步淹;
???????3.在attachAgent()內(nèi)部直接或通過反射來(lái)attachAgent();
???????4.native方法agent_init()及agent_release()方法來(lái)開啟及暫停jvmti诚撵。
???????so的加載會(huì)調(diào)用System.load(path)或System.loadLibrary(name)缭裆,兩者最后調(diào)用的都是同一個(gè)方法,執(zhí)行流程如下:
b.agentlib.cpp
#include <jni.h>
#include <string>
#include "jvmti.h"
#include "utils.h"
jvmtiEnv *mJvmtiEnv = NULL;
jlong tag = 0;
//初始化工作
extern "C"
JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *vm, char *options, void *reserved) {
//準(zhǔn)備jvmti環(huán)境
vm->GetEnv(reinterpret_cast<void **>(&mJvmtiEnv), JVMTI_VERSION_1_2);
//開啟jvmti的能力
jvmtiCapabilities caps;
//獲取所有的能力
mJvmtiEnv->GetPotentialCapabilities(&caps);
mJvmtiEnv->AddCapabilities(&caps);
return JNI_OK;
}
//調(diào)用System.Load()后會(huì)回調(diào)該方法
extern "C"
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
return JNI_VERSION_1_6;
}
void JNICALL objectAlloc(jvmtiEnv *jvmti_env, JNIEnv *jni_env, jthread thread,
jobject object, jclass object_klass, jlong size) {
//對(duì)象創(chuàng)建
}
void JNICALL objectFree(jvmtiEnv *jvmti_env, jlong tag) {
//對(duì)象釋放
}
void JNICALL methodEntry(jvmtiEnv *jvmti_env,JNIEnv* jni_env,jthread thread,jmethodID method) {
//方法進(jìn)入
}
void JNICALL methodExit(jvmtiEnv *jvmti_env,JNIEnv* jni_env,jthread thread,jmethodID method,jboolean was_popped_by_exception,
jvalue return_value) {
//方法退出
}
extern "C"
JNIEXPORT void JNICALL
Java_com_hly_memorymonitor_Monitor_agent_1init(JNIEnv *env, jclass jclazz) {
//開啟jvm事件監(jiān)聽
jvmtiEventCallbacks callbacks;
memset(&callbacks, 0, sizeof(callbacks));
callbacks.MethodEntry = &methodEntry;
callbacks.MethodExit = &methodExit;
callbacks.VMObjectAlloc = &objectAlloc;
callbacks.ObjectFree = &objectFree;
//設(shè)置回調(diào)函數(shù)
mJvmtiEnv->SetEventCallbacks(&callbacks, sizeof(callbacks));
//開啟監(jiān)聽
mJvmtiEnv->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_OBJECT_ALLOC, NULL);
mJvmtiEnv->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_OBJECT_FREE, NULL);
mJvmtiEnv->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_METHOD_ENTRY, NULL);
mJvmtiEnv->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_METHOD_EXIT, NULL);
env->ReleaseStringUTFChars(_path, path);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_hly_memorymonitor_Monitor_agent_1release(JNIEnv *env, jclass clazz) {
mJvmtiEnv->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_VM_OBJECT_ALLOC, NULL);
mJvmtiEnv->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_OBJECT_FREE, NULL);
mJvmtiEnv->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_METHOD_ENTRY, NULL);
mJvmtiEnv->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_METHOD_EXIT, NULL);
}
???????1.在Agent_OnAttach()內(nèi)部初始化寿烟,準(zhǔn)備jvmti環(huán)境幼驶,開啟及獲取能力;
???????2.在xx_agent_1init()內(nèi)部韧衣,開啟jvmti事件監(jiān)聽盅藻,設(shè)置需要關(guān)注的回調(diào)(該回調(diào)在jvmti.h內(nèi)部有詳細(xì)的定義,設(shè)置需要關(guān)注的即可畅铭,本案例關(guān)注了JVMTI_EVENT_VM_OBJECT_ALLOC氏淑、JVMTI_EVENT_OBJECT_FREE、JVMTI_EVENT_OBJECT_FREE硕噩、JVMTI_EVENT_METHOD_EXIT)假残,執(zhí)行SetEventNotificationMode JVMTI_ENABLE 開啟監(jiān)聽;
???????3.在xx_agent_1release()內(nèi)部炉擅,執(zhí)行SetEventNotificationMode JVMTI_DISABLE 關(guān)閉監(jiān)聽辉懒;
c.獲取事件信息
???????在對(duì)指定的事件監(jiān)聽之后,需要提取到需要的信息谍失,比如:創(chuàng)建了什么對(duì)象眶俩、釋放了什么對(duì)象、進(jìn)入了哪個(gè)方法快鱼、退出了哪個(gè)方法等等颠印。
???????附加一下獲取信息的方法:
void JNICALL objectAlloc(jvmtiEnv *jvmti_env, JNIEnv *jni_env, jthread thread,
jobject object, jclass object_klass, jlong size) {
//給對(duì)象打tag,后續(xù)在objectFree()內(nèi)可以通過該tag來(lái)判斷是否成對(duì)出現(xiàn)釋放
tag += 1;
jvmti_env->SetTag(object, tag);
//獲取線程信息
jvmtiThreadInfo threadInfo;
jvmti_env->GetThreadInfo(thread, &threadInfo);
//獲得 創(chuàng)建的對(duì)象的類簽名
char *classSignature;
jvmti_env->GetClassSignature(object_klass, &classSignature, nullptr);
//獲得堆棧信息
char *stackInfo = createStackInfo(jvmti_env, jni_env, thread, 10);
ALOGE("object alloc, Thread is %s, class is %s, size is %s, tag is %lld, stackInfo is %s", threadInfo.name, classSignature, size, tag, stackInfo);
}
void JNICALL methodEntry(jvmtiEnv *jvmti_env,JNIEnv* jni_env,jthread thread,jmethodID method) {
jclass clazz;
char *signature;
char *methodName;
//獲得方法對(duì)應(yīng)的類
jvmti_env->GetMethodDeclaringClass(method, &clazz);
//獲得類的簽名
jvmti_env->GetClassSignature(clazz, &signature, 0);
//獲得方法名字
jvmti_env->GetMethodName(method, &methodName, NULL, NULL);
ALOGE("methodEntry method name is %s", methodName);
jvmti_env->Deallocate((unsigned char *)methodName);
jvmti_env->Deallocate((unsigned char *)signature);
}
d.存文件
???????為了效率性抹竹,可以通過mmap來(lái)實(shí)現(xiàn)文件的寫入线罕,代碼如下:
#include <cstdint>
#include <unistd.h>
#include <cstring>
#include <fcntl.h>
#include <sys/mman.h>
#include "MemoryFile.h"
//系統(tǒng)給我們提供真正的內(nèi)存時(shí),用頁(yè)為單位提供
//內(nèi)存分頁(yè)大小 一分頁(yè)的大小
int32_t DEFAULT_FILE_SIZE = getpagesize();
MemoryFile::MemoryFile(const char *path) {
m_path = path;
m_fd = open(m_path, O_RDWR | O_CREAT, S_IRWXU);
m_size = DEFAULT_FILE_SIZE;
//將文件設(shè)置為m_size大小
ftruncate(m_fd, m_size);
//mmap內(nèi)存映射
m_ptr = static_cast<int8_t *>(mmap(0, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0));
//初始化m_actualSize為0
m_actualSize = 0;
}
MemoryFile::~MemoryFile() {
munmap(m_ptr, m_size);
close(m_fd);
}
void MemoryFile::write(char *data, int dataLen) {
if (m_actualSize + dataLen >= m_size) {
resize(m_actualSize + dataLen);
}
//將data的dataLen長(zhǎng)度的數(shù)據(jù) 拷貝到 m_ptr + m_actualSize;
memcpy(m_ptr + m_actualSize, data, dataLen);//操作內(nèi)存窃判,通過內(nèi)存映射就寫入文件了
//重新設(shè)置最初位置
m_actualSize += dataLen;
}
void MemoryFile::resize(int32_t needSize) {
int32_t oldSize = m_size;
do {
m_size *= 2;
} while (m_size < needSize);
//設(shè)置文件大小
ftruncate(m_fd, m_size);
//解除映射
munmap(m_ptr, oldSize);
//重新進(jìn)行mmap內(nèi)存映射
m_ptr = static_cast<int8_t *>(mmap(0, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0));
}
???????本文介紹了jvmti的使用過程及對(duì)對(duì)象創(chuàng)建釋放钞楼、方法進(jìn)入退出事件的監(jiān)聽,最后對(duì)事件信息存文件袄琳,這樣當(dāng)應(yīng)用因?yàn)閮?nèi)存使用不當(dāng)導(dǎo)致的問題询件,通過文件就可以分析出來(lái)燃乍。