Android内存篇(一)

您所在的位置:网站首页 安卓内存监控 Android内存篇(一)

Android内存篇(一)

2024-07-09 18:14| 来源: 网络整理| 查看: 265

前言

一般产品或项目前期都是以快速实现,上线的方式来完成,在生产环境中再开始进行优化,而Android的APP优化,比较重点的还是内存优化,因为每个APP都分配的最大内存,像内存泄露,内存抖动等慢慢都会让APP出来OOM崩溃的情况,最近也是一直在学习和研究内存优化这块,也是在实践中记录笔记。

JVMTI

JVMTI 本质上是在JVM内部的许多事件进行了埋点,通过这些埋点可以给外部提供当前上下文的一些信息。

从 Android 8.0 开始,Android ART已经加入了JVMTI的相关功能。目录位于art/runtime/openjdkjvmti下,从Android.bp可以看到,编译会生成libopenjdkjvmtid.so、libopenjdkjvmti.so文件,其中核心文件是jvmti.h文件,里面定义了一些核心方法和结构体。本地实现时,需要引入该文件来实现对应的Capabilities。

看到.so文件,很明显就是想使用JVMTI,就要用JNI的方式去进行调用了,接下来我们直接从代码上实现。

代码实现

因为要使用JNI,所以项目要创建一个Native C++的项目,完整的Demo源码会在文章最后放出来。

项目目录01创建Monitor监听类

监听类里面主要就是初始化JVMTI,包括启动和释放,另外加入一个过滤的函数,使用JVMTI监听时,会将所有的对象和方法都列出来,做为线上监听,我们需要写入本地文件里到时可以查看,如果所有的方法都写入,文件会特别大,所以加了一个函数用于只写入我们想要得到的信息。

attachAgent开启JVMTI

代码attachAgent函数是初始化JVMTI的使用,在Android9.0中已将API添加到framework/base/core/java/android/os/Debug.java中,可以直接调用,而Android9.0以下的,需要通过反射的方法进行调用。

JNI方法

定义了三个JNI的方法,用于初始化,释放和过滤要存文件的内容,具体的实现在native-lib.cpp中。

Moniter代码代码语言:javascript复制package pers.vaccae.memorymonitor import android.content.Context import android.os.Build import android.os.Debug import android.util.Log import java.io.File import java.nio.file.Files import java.nio.file.Paths import java.text.SimpleDateFormat import java.util.* /** * 作者:Vaccae * 邮箱:[email protected] * 创建时间:15:13 * 功能模块说明: */ object Monitor { private const val LIB_NAME = "libmemorymonitor.so" fun init(context: Context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { //查找SO的路径 val libDir: File = File(context.filesDir, "lib") if (!libDir.exists()) { libDir.mkdirs() } //判断So库是否存在,不存在复制过来 val libSo: File = File(libDir, LIB_NAME) if (libSo.exists()) libSo.delete() val findLibrary = ClassLoader::class.java.getDeclaredMethod("findLibrary", String::class.java) val libFilePath = findLibrary.invoke(context.classLoader, "memorymonitor") as String Log.i("jvmti", "so Path:$libFilePath") Files.copy( Paths.get(File(libFilePath).absolutePath), Paths.get( libSo.absolutePath ) ) //加载SO库 val agentPath = libSo.absolutePath System.load(agentPath) //agent连接到JVMTI attachAgent(agentPath, context.classLoader); //开启JVMTI事件监听 val logDir = File(context.filesDir, "log") if (!logDir.exists()) logDir.mkdir() //获取当前时间 val formatter = SimpleDateFormat("yyyyMMddHHmmss") val curDate= formatter.format(Date(System.currentTimeMillis())) val path = "${logDir.absolutePath}/${curDate}.log" attachInit(path) } else { Log.i("jvmti", "系统版本无法全用JVMTI") } } //agent连接到JVMTI private fun attachAgent(agentPath: String, classLoader: ClassLoader) { //Android 9.0+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { Debug.attachJvmtiAgent(agentPath, null, classLoader) } else { //android 9.0以下版本使用反射方式加载 val vmDebugClazz = Class.forName("dalvik.system.VMDebug") val attachAgentMethod = vmDebugClazz.getMethod("attachAgent", String::class.java) attachAgentMethod.isAccessible = true attachAgentMethod.invoke(null, agentPath) } } fun release() { attachRelease() } fun writeFilters(pkgname:String){ Log.i("jvmti",pkgname) attachWFilters(pkgname) } //region JNI函数 //开启JVMTI事件监听 private external fun attachInit(path: String) private external fun attachRelease() private external fun attachWFilters(packagename: String) //endregion }02拷贝jvmti.h文件

Android的安装目录下有JDK,如果自己安装的JDK,也可以在安装的JDK目录的include下看到jvmti.h的头文件,将这个jvmti.h的头文件拷贝到程序目录cpp下。

当attacchAgent开启监听后,会执行一个回调函数,可以在jvmti.h中看到,我们在C++文件中写这个回调方法的实现用于加载要监听的东西的参数配置

像监听的回调方法,也是在这个头文件中找到,这次我们就监听对象的创建和函数的调用两个方法,如下:

03C++ nativ-lib中实现回调

在jvmti.h中拷过来后可以看到相关的回调函数了,在native-lib.cpp中主要就是写三个回调方法的实现。

Agent_OnAttach(初始化回调)

objectAlloc(对象创建时的回调)

methodEntry(函数进入时的回调)

JNI attachInit实现初始化的函数

native-lib.cpp完整代码代码语言:javascript复制#include #include #include #include #include "jvmti.h" #include "MemoryFile.h" #define LOG_TAG "jvmti" #define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) #define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) #define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) #define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) jvmtiEnv *mJvmtiEnv; MemoryFile *memoryFile; jlong tag = 0; std::string mPackageName; //查找过滤 jboolean findFilter(const char *name) { std::string tmpstr = name; int idx; //先判断甩没有Error,有Error直接输出 idx = tmpstr.find(mPackageName); if (idx == std::string::npos) { idx = tmpstr.find("OutOfMemoryError"); if (idx == std::string::npos)//不存在。 { return JNI_FALSE; } else { return JNI_TRUE; } } else { return JNI_TRUE; } } // 获取当时系统时间 std::string GetCurrentSystemTime() { //auto t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); auto now = std::chrono::system_clock::now(); //通过不同精度获取相差的毫秒数 uint64_t dis_millseconds = std::chrono::duration_cast(now.time_since_epoch()).count() - std::chrono::duration_cast(now.time_since_epoch()).count() * 1000; time_t tt = std::chrono::system_clock::to_time_t(now); struct tm *ptm = localtime(&tt); char date[60] = {0}; sprintf(date, "%d-%02d-%02d %02d:%02d:%02d.%03d", (int) ptm->tm_year + 1900, (int) ptm->tm_mon + 1, (int) ptm->tm_mday, (int) ptm->tm_hour, (int) ptm->tm_min, (int) ptm->tm_sec, (int) dis_millseconds); return move(std::string(date)); } jvmtiEnv *CreateJvmtiEnv(JavaVM *vm) { jvmtiEnv *jvmti_env; jint result = vm->GetEnv((void **) &jvmti_env, JVMTI_VERSION_1_2); if (result != JNI_OK) { ALOGI("CreateJvmtiEnv is NULL"); return nullptr; } return jvmti_env; } //调用System.Load()后会回调该方法 extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { JNIEnv *env; ALOGI("JNI_OnLoad"); if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) { return JNI_ERR; } ALOGI("JNI_OnLoad Finish"); return JNI_VERSION_1_6; } void JNICALL objectAlloc(jvmtiEnv *jvmti_env, JNIEnv *jni_env, jthread thread, jobject object, jclass object_klass, jlong size) { //给对象打tag,后续在objectFree()内可以通过该tag来判断是否成对出现释放 tag += 1; jvmti_env->SetTag(object, tag); //获取线程信息 jvmtiThreadInfo threadInfo; jvmti_env->GetThreadInfo(thread, &threadInfo); //获得 创建的对象的类签名 char *classSignature; jvmti_env->GetClassSignature(object_klass, &classSignature, nullptr); if (mPackageName.empty() || findFilter(classSignature)) { //写入日志文件 char str[500]; char *format = "%s: object alloc {Thread:%s Class:%s Size:%lld Tag:%lld} \r\n"; //ALOGI(format, GetCurrentSystemTime().c_str(),threadInfo.name, classSignature, size, tag); sprintf(str, format, GetCurrentSystemTime().c_str(), threadInfo.name, classSignature, size, tag); memoryFile->write(str, sizeof(char) * strlen(str)); } jvmti_env->Deallocate((unsigned char *) classSignature); } void JNICALL methodEntry(jvmtiEnv *jvmti_env, JNIEnv *jni_env, jthread thread, jmethodID method) { jclass clazz; char *signature; char *methodName; //获得方法对应的类 jvmti_env->GetMethodDeclaringClass(method, &clazz); //获得类的签名 jvmti_env->GetClassSignature(clazz, &signature, nullptr); //获得方法名字 jvmti_env->GetMethodName(method, &methodName, nullptr, nullptr); if (mPackageName.empty() || findFilter(signature)) { //写日志文件 char str[500]; char *format = "%s: methodEntry {%s %s} \r\n"; //ALOGI(format, GetCurrentSystemTime().c_str(), signature, methodName); sprintf(str, format, GetCurrentSystemTime().c_str(), signature, methodName); memoryFile->write(str, sizeof(char) * strlen(str)); } jvmti_env->Deallocate((unsigned char *) methodName); jvmti_env->Deallocate((unsigned char *) signature); } extern "C" JNIEXPORT void JNICALL Java_pers_vaccae_memorymonitor_Monitor_attachInit(JNIEnv *env, jobject thiz, jstring path) { ALOGI("attachInit"); const char *_path = env->GetStringUTFChars(path, NULL); ALOGI("mPackageName:%s", mPackageName.c_str()); memoryFile = new MemoryFile(_path); //开启JVMTI事件监听 jvmtiEventCallbacks callbacks; memset(&callbacks, 0, sizeof(callbacks)); callbacks.VMObjectAlloc = &objectAlloc; callbacks.MethodEntry = &methodEntry; ALOGI("SetEventCallbacks"); //设置回调函数 int error = mJvmtiEnv->SetEventCallbacks(&callbacks, sizeof(callbacks)); ALOGI("返回码:%d\n", error); //开启监听 mJvmtiEnv->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_OBJECT_ALLOC, nullptr); mJvmtiEnv->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_METHOD_ENTRY, nullptr); env->ReleaseStringUTFChars(path, _path); ALOGI("attachInit Finished"); } //初始化工作 extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *vm, char *options, void *reserved) { int error; //准备JVMTI环境 mJvmtiEnv = CreateJvmtiEnv(vm); //开启JVMTI的能力 jvmtiCapabilities caps; mJvmtiEnv->GetPotentialCapabilities(&caps); mJvmtiEnv->AddCapabilities(&caps); ALOGI("Agent_OnAttach Finish"); return JNI_OK; } extern "C" JNIEXPORT void JNICALL Java_pers_vaccae_memorymonitor_Monitor_attachRelease(JNIEnv *env, jobject thiz) { delete memoryFile; //关闭监听 mJvmtiEnv->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_VM_OBJECT_ALLOC, NULL); mJvmtiEnv->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_METHOD_ENTRY, NULL); } extern "C" JNIEXPORT void JNICALL Java_pers_vaccae_memorymonitor_Monitor_attachWFilters(JNIEnv *env, jobject thiz, jstring packagename) { const char *_packagename = env->GetStringUTFChars(packagename, NULL); mPackageName = std::string(_packagename); env->ReleaseStringUTFChars(packagename, _packagename); }04日志写入文件MemoryFile

建一个MemoryFile的C++类,通过这个类实现消息往MemoryFIle中写入。

MemoryFile.h代码语言:javascript复制// // Created by 36574 on 2022-03-25. // #ifndef MEMORYMONITOR_MEMORYFILE_H #define MEMORYMONITOR_MEMORYFILE_H class MemoryFile { private: const char* m_path; int m_fd; int32_t m_size; int8_t *m_ptr; int m_actualSize; void resize(int32_t needSize); public: MemoryFile(const char *path); ~MemoryFile(); void write(char *data, int dataLen); }; #endif //MEMORYMONITOR_MEMORYFILE_H MemoryFile.cpp 代码语言:javascript复制// // Created by 36574 on 2022-03-25. // #include #include #include #include #include #include #include "MemoryFile.h" std::mutex mtx; //系统给我们提供真正的内存时,用页为单位提供 //内存分页大小 一分页的大小 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; //将文件设置为m_size大小 ftruncate(m_fd, m_size); //mmap内存映射 m_ptr = static_cast(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) { mtx.lock(); if(m_actualSize + dataLen >= m_size){ resize(m_actualSize+dataLen); } //将data的datalen长度的数据 拷贝到 m_ptr + m_actualSize; //操作内存,通过内存映射就写入文件了 memcpy(m_ptr + m_actualSize, data, dataLen); //重新设置最初位置 m_actualSize += dataLen; mtx.unlock(); } void MemoryFile::resize(int32_t needSize) { int32_t oldSize = m_size; do{ m_size *=2; } while (m_size


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3