GG修改器破解版下载地址:https://ghb2023zs.bj.bcebos.com/gg/xgq/ggxgq?GGXGQ
大家好,今天小编为大家分享关于gg修改器要用root吗_gg修改器一定要root吗的内容,赶快来一起来看看吧。
Matrix-android 当前监控范围包括:应用安装包大小、帧率变化、启动耗时、卡顿、慢方法、SQLite操作优化、文件读写、内存泄漏等。整个库主要由 5 个组件构成:
使用Matrix 的使用方式很简单,在 Application 中初始化后启动即可:
Matrix.Builder builder = new Matrix.Builder(this);
// 添加需要的插件
builder.plugin(new TracePlugin(...));
builder.plugin(new ResourcePlugin(...));
builder.plugin(new IOCanaryPlugin(...));
builder.plugin(new SQLiteLintPlugin(...));
// 初始化
Matrix matrix = Matrix.init(builder.build());
// 启动
matrix.startAllPlugins();
Matrix 类相当于整个库的统一对外接口,资源监控、IO 监控、卡顿监控等功能实现是由其它具体的Plugin 完成的。
也可以不在 Application 中启动全部插件,而是在某个场景中启动特定的插件,比如:
public class TestTraceMainActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
// 启动插件
Plugin plugin = Matrix.with().getPluginByClass(TracePlugin.class);
if (!plugin.isPluginStarted()) {
plugin.start();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
// 停止插件
Plugin plugin = Matrix.with().getPluginByClass(TracePlugin.class);
if (plugin.isPluginStarted()) {
plugin.stop();
}
}
}
每个具体的 Plugin 都会实现 IPlugin 接口:
public interface IPlugin {
Application getApplication();
void init(Application application, PluginListener pluginListener);
void start();
void stop();
void destroy();
String getTag();
// 在应用可见/不可见时回调
void onForeground(boolean isForeground);
}
可以通过 PluginListener 监听 Plugin 的生命周期变化,或在 Plugin 上报问题时回调:
public interface PluginListener {
void onInit(Plugin plugin);
void onStart(Plugin plugin);
void onStop(Plugin plugin);
void onDestroy(Plugin plugin);
void onReportIssue(Issue issue);
}
上报的问题使用实体类 Issue 包装,Issue 包含 tag、type 等通用字段,详细信息可以通过 JSON 对象content 获取:
public class Issue {
private int type;
private String tag;
private String key;
private JSONObject content;
private Plugin plugin;
}
Matrix
Matrix 是一个单例类,在构造函数执行时,Matrix 内部的所有 Plugin 都会被初始化:
public class Matrix {
private final HashSet<Plugin> plugins;
private Matrix(Application app, PluginListener listener, HashSet<Plugin> plugins) {
this.plugins = plugins;
AppActiveMatrixDelegate.INSTANCE.init(application); // 下面会分析
// 初始化所有 Plugin,并回调 pluginListener
for (Plugin plugin : plugins) {
plugin.init(application, pluginListener);
pluginListener.onInit(plugin);
}
}
}
Plugin 是一个抽象类,每次执行 init / start / stop / destroy 等方法时都会更新状态,并回调PluginListener:
public abstract class Plugin implements IPlugin,
IssuePublisher.OnIssueDetectListener, IAppForeground {
private int status = PLUGIN_CREATE;
@Override
public void init(Application app, PluginListener listener) {
status = PLUGIN_INITED;
AppActiveMatrixDelegate.INSTANCE.addListener(this); // 下面会分析
}
@Override
public void start() {
status = PLUGIN_STARTED;
pluginListener.onStart(this);
}
...
}
如果某个具体 Plugin 上报了一个问题,父类 Plugin 还会对该 Issue 填充 tag、type、process、time等通用字段,并回调 PluginListener 的 onReportIssue 方法:
@Override
public void onDetectIssue(Issue issue) {
issue.setPlugin(this);
JSONObject content = issue.getContent();
// 拼接 tag、type、process、time 等通用字段
content.put(Issue.ISSUE_REPORT_TAG, issue.getTag());
content.put(Issue.ISSUE_REPORT_TYPE, issue.getType());
content.put(Issue.ISSUE_REPORT_PROCESS,
MatrixUtil.getProcessName(application));
content.put(Issue.ISSUE_REPORT_TIME, System.currentTimeMillis());
// 回调
pluginListener.onReportIssue(issue);
}
Matrix 和 Plugin 都监听了 AppActiveMatrixDelegate,它的主要作用是在应用可见/不可见时通知观察者:
public enum AppActiveMatrixDelegate {
INSTANCE; // 单例
// 观察者列表
private final Set<IAppForeground> listeners = new HashSet();
// 应用可见时通知观察者
private void onDispatchForeground(String visibleScene) {
handler.post(() -> {
isAppForeground = true;
synchronized (listeners) {
for (IAppForeground listener : listeners) {
listener.onForeground(true);
}
}
}
}
// 应用不可见时通知观察者,逻辑和上面的一样
private void onDispatchBackground(String visibleScene) {
...
}
}
判断应用是否可见的逻辑是通过 ActivityLifecycleCallbacks 接口实现的:
private final class Controller implements
Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {
@Override
public void onActivityStarted(Activity activity) {
// 应用可见
updateScene(activity);
onDispatchForeground(getVisibleScene());
}
@Override
public void onActivityStopped(Activity activity) {
// 没有可见的 Activity 了,相当于进入了后台
if (getTopActivityName() == null) {
onDispatchBackground(getVisibleScene());
}
}
...
@Override
public void onTrimMemory(int level) {
// 应用 UI 不可见
if (level == TRIM_MEMORY_UI_HIDDEN && isAppForeground) { // fallback
onDispatchBackground(visibleScene);
}
}
}
Matrix-android 主要包含 5 个组件:APK Checker、Resource Canary、Trace Canary、IO Canary、SQLite Lint。其中 APK Checker 独立运行;其它 4 个模块需要在 Application 中,通过统一对外接口Matrix 配置完成后执行。每一个模块相当于一个 Plugin,在执行初始化、启动、停止、销毁、报告问题等操作时,都会回调 PluginListener,并更新状态。
每一个 Issue 都有 tag、type、process、time 等 4 个通用字段。
可以监听 AppActiveMatrixDelegate,在应用可见/不可见时,回调 onForeground 方法,以及执行相应的操作。应用可见指的是存在可见的 Activity,应用不可见指的是没有可见的 Activity,或者内存不足了,应用的 UI 不可见
Matrix 的内存泄漏监控是由 ResourceCanary 实现的,准确的说,ResourceCanary 只能实现 Activity的内存泄漏检测,但在出现 Activity 内存泄漏时,可以选择 dump 一个堆转储文件,通过该文件,可以分析应用是否存在重复的 Bitmap。
ResourceCanary 是基于 WeakReference 特性和 Square Haha 库开发的 Activity 泄漏和 Bitmap 重复创建检测工具,使用之前,需要进行如下配置:
Matrix.Builder builder = new Matrix.Builder(this);
// 用于在用户点击生成的问题通知时,通过这个 Intent 跳转到指定的 Activity
Intent intent = new Intent();
intent.setClassName(this.getPackageName(),
"com.tencent.mm.ui.matrix.ManualDumpActivity");
ResourceConfig resourceConfig = new ResourceConfig.Builder()
.dynamicConfig(new DynamicConfigImplDemo()) // 用于动态获取一些自定义的选项, 不同 Plugin 有不同的选项
.setAutoDumpHprofMode(ResourceConfig.DumpMode.AUTO_DUMP) // 自动生成 Hprof 文件 //
.setDetectDebuger(true) //matrix test code
.setNotificationContentIntent(intent) // 问题通知
.build();
builder.plugin(new ResourcePlugin(resourceConfig));
// 这个类可用于修复一些内存泄漏问题
ResourcePlugin.activityLeakFixer(this);
如果想要在具体的 Activity 中检测内存泄漏,那么获取 Plugin 并执行 start方法(一般在 onCreate 方法中执行)即可:
Plugin plugin = Matrix.with().getPluginByClass(ResourcePlugin.class);
if (!plugin.isPluginStarted()) {
plugin.start();
}
捕获到问题后,会上报信息如下:
{
"tag": "memory",
"type": 0,
"process": "sample.tencent.matrix",
"time": 1590396618440,
"activity": "sample.tencent.matrix.resource.TestLeakActivity",
}
如果 DumpMode 为 AUTO_DUMP,还会生成一个压缩文件,里面包含一个堆转储文件和一个result.info 文件,可以根据 result.info 文件发现具体是哪一个 Activity 泄漏了:
{
"tag":"memory",
"process":"com.tencent.mm", "resultZipPath":"/storage/emulated/0/Android/.tencent.mm/cache/matrix_r esource/dump_result_17400_20170713183615.zip",
"activity":"com.tencent.mm.plugin.setting.ui.setting.SettingsUI",
}
ResourcePlugin 执行之前,需要通过 ResourceConfig 配置,配置选项有:
public static final class Builder {
private DumpMode mDefaultDumpHprofMode = DEFAULT_DUMP_HPROF_MODE;
private IDynamicConfig dynamicConfig;
private Intent mContentIntent;
private boolean mDetectDebugger = false;
}
其中, ContentIntent 用于发送通知。
DumpMode 用于控制检测到问题后的行为,可选值有:
public interface IDynamicConfig {
String get(String key, String defStr);
int get(String key, int defInt);
long get(String key, long defLong);
boolean get(String key, boolean defBool);
float get(String key, float defFloat);
}
和 Resource Canary 相关的选项有:
enum ExptEnum {
//resource
clicfg_matrix_resource_detect_interval_millis, // 后台线程轮询间隔
clicfg_matrix_resource_detect_interval_millis_bg, // 应用不可见时的轮询间隔
clicfg_matrix_resource_max_detect_times, // 重复检测多次后才认为出现了内存泄漏,避 免误判
clicfg_matrix_resource_dump_hprof_enable, // 没见代码有用到
}
实现该接口对应的方法,即可通过 ResourceConfig 获取上述选项的值:
public final class ResourceConfig {
// 后台线程轮询间隔默认为 1min
private static final long DEFAULT_DETECT_INTERVAL_MILLIS = TimeUnit.MINUTES.toMillis(1);
// 应用不可见时,后台线程轮询间隔默认为 1min
private static final long DEFAULT_DETECT_INTERVAL_MILLIS_BG = TimeUnit.MINUTES.toMillis(20);
// 默认重复检测 10 次后,如果依然能获取到 Activity ,才认为出现了内存泄漏
private static final int DEFAULT_MAX_REDETECT_TIMES = 10;
public long getScanIntervalMillis() { ... }
public long getBgScanIntervalMillis() { ... }
public int getMaxRedetectTimes() { ... }
}
在开始监测 Activity 内存泄漏之前,Resource Canary 首先会尝试修复可能的内存泄漏问题,它是通过监听 ActivityLifeCycleCallbacks 实现的,在 Activity 回调 onDestroy 时,它会尝试解除 Activity 和InputMethodManager、View 之间的引用关系:
public static void activityLeakFixer(Application application) {
application.registerActivityLifecycleCallbacks(new ActivityLifeCycleCallbacksAdapter() {
@Override
public void onActivityDestroyed(Activity activity) {
ActivityLeakFixer.fixInputMethodManagerLeak(activity);
ActivityLeakFixer.unbindDrawables(activity);
}
});
}
对于 InputMethodManager,它可能引用了 Activity 中的某几个 View,因此,将它和这几个 View 解除引用关系即可:
public static void fixInputMethodManagerLeak(Context destContext) {
final InputMethodManager imm = (InputMethodManager)
destContext.getSystemService(Context.INPUT_METHOD_SERVICE);
final String[] viewFieldNames = new String[]{"mCurRootView", "mServedView", "mNextServedView"};
for (String viewFieldName : viewFieldNames) {
final Field paramField = imm.getClass().getDeclaredField(viewFieldName);
...
// 如果 IMM 引用的 View 引用了该 Activity,则切断引用关系
if (view.getContext() == destContext) {
paramField.set(imm, null);
}
}
}
对于 View,它可能通过监听器或 Drawable 的形式关联 Activity,因此,我们需要把每一个可能的引用关系解除掉:
public static void unbindDrawables(Activity ui) {
final View viewRoot = ui.getWindow().peekDecorView().getRootView();
unbindDrawablesAndRecycle(viewRoot);
}
private static void unbindDrawablesAndRecycle(View view) {
// 解除通用的 View 引用关系
recycleView(view);
// 不同类型的 View 可能有不同的引用关系,一一处理即可
if (view instanceof ImageView) {
recycleImageView((ImageView) view);
}
if (view instanceof TextView) {
recycleTextView((TextView) view);
}
...
}
// 将 Listener、Drawable 等可能存在的引用关系切断
private static void recycleView(View view) {
view.setOnClickListener(null);
view.setOnFocusChangeListener(null);
view.getBackground().setCallback(null);
view.setBackgroundDrawable(null);
...
}
具体的监测工作,ResourcePlugin 交给了 ActivityRefWatcher 来完成。
ActivityRefWatcher 主要的三个方法:start、stop、destroy 分别用于启动监听线程、停止监听线程、结束监听。以 start 为例:
public class ActivityRefWatcher extends FilePublisher implements Watcher, IAppForeground {
@Override
public void start() {
stopDetect();
final Application app = mResourcePlugin.getApplication();
if (app != null) {
// 监听 Activity 的 onDestroy 回调,记录 Activity 信息
app.registerActivityLifecycleCallbacks(mRemovedActivityMonitor);
// 监听 onForeground 回调,以便根据应用可见状态修改轮询间隔时长
AppActiveMatrixDelegate.INSTANCE.addListener(this);
// 启动监听线程
scheduleDetectProcedure();
}
}
}
其中 mRemovedActivityMonitor 用于在 Activity 回调 onDestroy 时记录 Activity 信息,主要包括Activity 的类名和一个根据 UUID 生成的 key:
// 用于记录 Activity 信息
private final ConcurrentLinkedQueue<DestroyedActivityInfo>
mDestroyedActivityInfos;
private final Application.ActivityLifecycleCallbacks mRemovedActivityMonitor = new ActivityLifeCycleCallbacksAdapter() {
@Override
public void onActivityDestroyed(Activity activity) {
pushDestroyedActivityInfo(activity);
}
};
// 在 Activity 销毁时,记录 Activity 信息
private void pushDestroyedActivityInfo(Activity activity) {
final String activityName = activity.getClass().getName();
final UUID uuid = UUID.randomUUID();
final String key = keyBuilder.toString(); // 根据 uuid 生成
final DestroyedActivityInfo destroyedActivityInfo = new DestroyedActivityInfo(key, activity, activityName);
mDestroyedActivityInfos.add(destroyedActivityInfo);
}
DestroyedActivityInfo 包含信息如下:
public class DestroyedActivityInfo {
public final String mKey; // 根据 uuid 生成
public final String mActivityName; // 类名
public final WeakReference<Activity> mActivityRef; // 弱引用
public int mDetectedCount = 0; // 重复检测次数,默认检测 10 次后,依然能通过弱引用获 取,才认为发生了内存泄漏
}
线程启动后,应用可见时,默认每隔 1min(通过 IDynamicConfig 指定) 将轮询任务发送到默认的后台线程(MatrixHandlerThread)执行:
// 自定义的线程切换机制,用于将指定的任务延时发送到主线程/后台线程执行
private final RetryableTaskExecutor mDetectExecutor;
private ActivityRefWatcher(...) {
HandlerThread handlerThread = MatrixHandlerThread.getDefaultHandlerThread();
mDetectExecutor = new RetryableTaskExecutor(config.getScanIntervalMillis(), handlerThread);
}
private void scheduleDetectProcedure() {
// 将任务发送到 MatrixHandlerThread 执行
mDetectExecutor.executeInBackground(mScanDestroyedActivitiesTask);
}
下面看轮询任务 mScanDestroyedActivitiesTask,它是一个内部类,代码很长,我们一点一点分析
首先,在上一篇文章关于原理的部分介绍过,ResourceCanary 会设置了一个哨兵元素,检测是否真的执行了 GC,如果没有,它不会往下执行:
private final RetryableTask mScanDestroyedActivitiesTask = new RetryableTask() {
@Override
public Status execute() {
// 创建指向一个临时对象的弱引用
final WeakReference<Object> sentinelRef = new WeakReference<>(new Object());
// 尝试触发 GC
triggerGc();
// 检测弱引用指向的对象是否存活来判断虚拟机是否真的执行了GC
if (sentinelRef.get() != null) {
// System ignored our gc request, we will retry later.
return Status.RETRY;
}
...
return Status.RETRY; // 返回 retry,这个任务会一直执行
}
};
private void triggerGc() {
Runtime.getRuntime().gc();
Runtime.getRuntime().runFinalization();
}
接着,遍历所有 DestroyedActivityInfo,并标记该 Activity,避免重复报:
final Iterator<DestroyedActivityInfo> infoIt =
mDestroyedActivityInfos.iterator();
while (infoIt.hasNext()) {
if (!mResourcePlugin.getConfig().getDetectDebugger()
&& isPublished(destroyedActivityInfo.mActivityName) // 如果已标记,则跳 过
&& mDumpHprofMode != ResourceConfig.DumpMode.SILENCE_DUMP) {
infoIt.remove();
continue;
}
if (mDumpHprofMode == ResourceConfig.DumpMode.SILENCE_DUMP) {
if (mResourcePlugin != null &&
!isPublished(destroyedActivityInfo.mActivityName)) { // 如果已标记,则跳过
...
}
if (null != activityLeakCallback) { // 但还会回调 ActivityLeakCallback
activityLeakCallback.onLeak(destroyedActivityInfo.mActivityName, destroyedActivityInfo.mKey);
}
} else if (mDumpHprofMode == ResourceConfig.DumpMode.AUTO_DUMP) {
...
markPublished(destroyedActivityInfo.mActivityName); // 标记
} else if (mDumpHprofMode == ResourceConfig.DumpMode.MANUAL_DUMP) {
...
markPublished(destroyedActivityInfo.mActivityName); // 标记
} else { // NO_DUMP
...
markPublished(destroyedActivityInfo.mActivityName); // 标记
}
}
同时,在重复检测大于等于 mMaxRedetectTimes 次时(由 IDynamicConfig指定,默认为 10),如果还能获取到该 Activity 的引用,才会认为出现了内存泄漏问题:
while (infoIt.hasNext()) {
...
// 获取不到,Activity 已回收
if (destroyedActivityInfo.mActivityRef.get() == null) {
continue;
}
// Activity 未回收,可能出现了内存泄漏,但为了避免误判,需要重复检测多次,如果都能获取到 Activity,才认为出现了内存泄漏
// 只有在 debug 模式下,才会上报问题,否则只会打印一个 log
++destroyedActivityInfo.mDetectedCount;
if (destroyedActivityInfo.mDetectedCount < mMaxRedetectTimes
|| !mResourcePlugin.getConfig().getDetectDebugger()) {
MatrixLog.i(TAG, "activity with key [%s] should be recycled but actually still
"
+ "exists in %s times, wait for next detection to confirm.",
destroyedActivityInfo.mKey, destroyedActivityInfo.mDetectedCount);
continue;
}
}
需要注意的是,只有在 debug 模式下,才会上报问题,否则只会打印一个 log。上报问题
对于 silence_dump 和 no_dump 模式,它只会记录 Activity 名,并回调 onDetectIssue:
final JSONObject resultJson = new JSONObject();
resultJson.put(SharePluginInfo.ISSUE_ACTIVITY_NAME,
destroyedActivityInfo.mActivityName);
mResourcePlugin.onDetectIssue(new Issue(resultJson));
对于 manual_dump 模式,它会使用 ResourceConfig 指定的 Intent 生成一个通知:
...
Notification notification = buildNotification(context, builder);
notificationManager.notify(NOTIFICATION_ID, notification);
对于 auto_dump,它会自动生成一个 hprof 文件并对该文件进行分析:
final File hprofFile = mHeapDumper.dumpHeap();
final HeapDump heapDump = new HeapDump(hprofFile,
destroyedActivityInfo.mKey, destroyedActivityInfo.mActivityName);
mHeapDumpHandler.process(heapDump);
dumpHeap 方法做了两件事:生成一个文件,写入 Hprof 数据到文件中:
public File dumpHeap() {
final File hprofFile = mDumpStorageManager.newHprofFile();
Debug.dumpHprofData(hprofFile.getAbsolutePath());
}
之后 HeapDumpHandler 就会处理该文件:
protected AndroidHeapDumper.HeapDumpHandler createHeapDumpHandler(...) {
return new AndroidHeapDumper.HeapDumpHandler() {
@Override
public void process(HeapDump result) {
CanaryWorkerService.shrinkHprofAndReport(context, result);
}
};
}
处理流程如下:
private void doShrinkHprofAndReport(HeapDump heapDump) {
// 裁剪 hprof 文件
new HprofBufferShrinker().shrink(hprofFile, shrinkedHProfFile);
// 压缩裁剪后的 hprof 文件
zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipResFile)));
copyFileToStream(shrinkedHProfFile, zos);
// 删除旧文件
shrinkedHProfFile.delete(); hprofFile.delete();
// 上报结果
CanaryResultService.reportHprofResult(this, zipResFile.getAbsolutePath(), heapDump.getActivityName());
}
private void doReportHprofResult(String resultPath, String activityName) {
final JSONObject resultJson = new JSONObject();
resultJson.put(SharePluginInfo.ISSUE_RESULT_PATH, resultPath);
resultJson.put(SharePluginInfo.ISSUE_ACTIVITY_NAME, activityName);
Plugin plugin = Matrix.with().getPluginByClass(ResourcePlugin.class);
plugin.onDetectIssue(new Issue(resultJson));
}
可以看到,由于原始 hprof 文件很大,因此 Matrix 先对它做了一个裁剪优化,接着再压缩裁剪后的文件,并删除旧文件,最后回调 onDetectIssue,上报文件位置、Activity 名称等信息。
检测到内存泄漏问题后,ActivityRefWatcher 会打印日志如下:
activity with key
[MATRIX_RESCANARY_REFKEY_sample.tencent.matrix.resource.TestLeakActivity_...] was suspected to be a leaked instance. mode[AUTO_DUMP]
如果模式为 AUTO_DUMP,且设置了 mDetectDebugger 为 true,那么,还会生成一个 hprof 文件:
hprof: heap dump
"/storage/emulated/0/Android/data/sample.tencent.matrix/cache/matrix_resource/du
mp_*.hprof" starting...
裁剪压缩后在 /sdcard/data/[package name]/matrix_resource 文件夹下会生成一个 zip 文件,比如:
/storage/emulated/0/Android/data/sample.tencent.matrix/cache/matrix_resource/dum p_result_*.zip
zip 文件里包括一个 dump*shinked.hprof 文件和一个 result.info 文件,其中 result.info 包含设备信息和关键 Activity 的信息,比如:
# Resource Canary Result Infomation. THIS FILE IS IMPORTANT FOR THE ANALYZER !!
sdkVersion=23
manufacturer=vivo hprofEntry=dump_323ff84d95424d35b0f62ef6a3f95838_shrink.hprof
leakedActivityKey=MATRIX_RESCANARY_REFKEY_sample.tencent.matrix.resource.TestLea
kActivity_8c5f3e9db8b54a199da6cb2abf68bd12
拿到这个 zip 文件,输入路径参数,执行 matrix-resource-canary-analyzer 中的 CLIMain 程序,即可得到一个 result.json 文件:
{
"activityLeakResult": {
"failure": "null",
"referenceChain": ["static
sample.tencent.matrix.resource.TestLeakActivity testLeaks", ...,
"sample.tencent.matrix.resource.TestLeakActivity instance"],
"leakFound": true,
"className": "sample.tencent.matrix.resource.TestLeakActivity",
"analysisDurationMs": 185,
"excludedLeak": false
},
"duplicatedBitmapResult": {
"duplicatedBitmapEntries": [],
"mFailure": "null",
"targetFound": false,
"analyzeDurationMs": 387
}
}
注意,CLIMain 在分析重复 Bitmap 时,需要反射 Bitmap 中的 “mBuffer”字段,而这个字段在 API 26已经被移除了,因此,对于 API 大于等于 26 的设备,CLIMain 只能分析 Activity 内存泄漏,无法分析重复 Bitmap。
下面简单分析一下 CLIMain 的执行过程,它是基于 Square Haha 开发的,执行过程分为 5 步:
public final class CLIMain {
public static void main(String[] args) {
doAnalyze();
}
private static void doAnalyze() throws IOException {
// 从 result.info 文件中拿到 hprof 文件、sdkVersion 等信息,接着开始分析
analyzeAndStoreResult(tempHprofFile, sdkVersion, manufacturer, leakedActivityKey, extraInfo);
}
private static void analyzeAndStoreResult(...) {
// 分析 Activity 内存泄漏
ActivityLeakResult activityLeakResult = new ActivityLeakAnalyzer(leakedActivityKey, ).analyze(heapSnapshot);
// 分析重复 Bitmap
DuplicatedBitmapResult duplicatedBmpResult = new DuplicatedBitmapAnalyzer(mMinBmpLeakSize, excludedBmps).analyze(heapSnapshot);
// 生成 result.json 文件并写入结果
final File resultJsonFile = new File(outputDir, resultJsonName);
resultJsonPW.println(resultJson.toString());
// 输出重复的 Bitmap 图像
for (int i = 0; i < duplicatedBmpEntryCount; ++i) {
final BufferedImage img = BitmapDecoder.getBitmap(...);
ImageIO.write(img, "png", os);
}
}
}
Activity 内存泄漏检测的关键是找到最短引用路径,原理是:
Resource Canary 的实现原理
在监测的同时,Resource Canary 使用 ActivityLeakFixer 尝试修复内存泄漏问题,实现原理是切断InputMethodManager、View 和 Activity 的引用
hprof 文件处理
Activity 内存泄漏检测的关键是找到最短引用路径,原理是:
把所有未被回收的 Bitmap 的数据 buffer 取出来,然后先对比所有长度为 1 的 buffer,找出相同的,记录所属的 Bitmap 对象;再对比所有长度为 2 的、长度为 3 的 buffer……直到把所有 buffer 都比对完,这样就记录了所有冗余的 Bitmap 对象.
文字太多,下篇再分析:
4.Hprof文件分析
5.卡顿监控
6.卡顿监控源码解析
7.插桩
8.资源优化
9.I/O监控及原理解析
以上就是关于gg修改器要用root吗_gg修改器一定要root吗的全部内容,感谢大家的浏览观看,如果你喜欢本站的文章可以CTRL+D收藏哦。
最新gg修改器的使用教程,最新GG修改器的使用教程 大小:19.86MB4,411人安装 在玩游戏的过程中,我们总会遇到一些让我们不满意的情况,比如说没有重生点、没有足……
下载gg修改器如何激活root,如何用GG修改器激活root 大小:6.22MB4,071人安装 如果您是一名Android系统玩家,那么您一定会听说过GG修改器,这是一款非常常用的辅……
下载这是我的战争gg修改器中文_这是我的战争gg修改背包 大小:3.39MB5,410人安装 大家好,今天小编为大家分享关于这是我的战争gg修改器中文_这是我的战争gg修改背包……
下载虚拟空间gg修改器可用最新版,虚拟空间gg修改器可用最新版:一款让游戏更好玩的伟大工具 大小:17.24MB4,519人安装 虚拟空间gg修改器可用最新版是一款深受游戏玩家们欢迎的游戏辅助工具。它可以帮助玩……
下载gg游戏修改器破解版下载,为什么你需要下载 gg游戏修改器破解版? 大小:13.88MB4,446人安装 如果你是一个玩游戏的人,那么你肯定知道 gg游戏修改器。它是一个功能强大的修改工……
下载用gg修改器弄root权限,用gg修改器弄root权限,畅享手机的极限操作 大小:4.41MB4,156人安装 现在,手机已经成为人们生活中不可或缺的一部分。手机的功能越来越强大,但是出厂时……
下载gg游戏修改器韩信, gg游戏修改器韩信:打破游戏局限,开创你的游戏世界 大小:8.87MB4,199人安装 作为一名游戏爱好者,相信大家都希望在游戏中能够尽情地发挥自己的操作水平和想象力……
下载gg修改器能不root权限_gg修改器无root权限 大小:5.79MB5,335人安装 大家好,今天小编为大家分享关于gg修改器能不root权限_gg修改器无root权限的内容,……
下载gg修改器最新无限技能,gg修改器:最新大杀器,激爽无限技能! 大小:7.03MB4,119人安装 在游戏世界里,玩家们总是追求更多的胜利和成就,而无限技能可以带来更强大的实力和……
下载gg游戏修改器拳皇97ol,GG游戏修改器:让你在《拳皇97OL》中成为无敌王者 大小:17.72MB4,578人安装 《拳皇97OL》是一款风靡全球的经典格斗手游,但有时候不免会遇到一些挑战难度过大的……
下载