文末
当你打算跳槽的时候,应该把“跳槽成功后,我能学到什么东西?对我的未来发展有什么好处”放在第一位。这些东西才是真正引导你的关键。在跳槽之前尽量“物尽其用”,把手头上的工作做好,最好是完成了某个项目或是得到提升之后再走。跳槽不是目的,而是为了达到最终职业目标的手段
最后祝大家工作升职加薪,面试拿到心仪Offer
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
String mainProcess = packageInfo.applicationInfo.processName;
ComponentName component = new ComponentName(context, serviceClass);
ServiceInfo serviceInfo;
try {
serviceInfo = packageManager.getServiceInfo(component, 0);
} catch (PackageManager.NameNotFoundException ignored) {
// Service is disabled.
return false;
}
if (serviceInfo.processName.equals(mainProcess)) {
CanaryLog.d(“Did not expect service %s to run in main process %s”, serviceClass, mainProcess);
// Technically we are in the service process, but we’re not in the service dedicated process.
return false;
}
int myPid = android.os.Process.myPid();
ActivityManager activityManager =
(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager.RunningAppProcessInfo myProcess = null;
List<ActivityManager.RunningAppProcessInfo> runningProcesses =
activityManager.getRunningAppProcesses();
if (runningProcesses != null) {
for (ActivityManager.RunningAppProcessInfo process : runningProcesses) {
if (process.pid == myPid) {
myProcess = process;
break;
}
}
}
if (myProcess == null) {
CanaryLog.d(“Could not find running process for %d”, myPid);
return false;
}
return myProcess.processName.equals(serviceInfo.processName);
}
源码分析
SDK初始化
mRefWatcher = LeakCanary.install(this);
这个是SDK向外暴露的方法,我们以此为入口进行源码的分析
public static RefWatcher install(Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
public static AndroidRefWatcherBuilder refWatcher(Context context) {
return new AndroidRefWatcherBuilder(context);
}
install
方法首先初始化了一个AndroidRefWatcherBuilder
类,然后通过listenerServiceClass
方法设置了DisplayLeakService
,这个类主要用于分析内存泄露的结果信息,然后发送通知给用户
public final class AndroidRefWatcherBuilder extends RefWatcherBuilder {
/**
- Sets a custom {@link AbstractAnalysisResultService} to listen to analysis results. This
- overrides any call to {@link #heapDumpListener(HeapDump.Listener)}.
*/
public AndroidRefWatcherBuilder listenerServiceClass(
Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));
}
…
}
public class RefWatcherBuilder<T extends RefWatcherBuilder> {
…
/** @see HeapDump.Listener */
public final T heapDumpListener(HeapDump.Listener heapDumpListener) {
this.heapDumpListener = heapDumpListener;
return self();
}
…
}
然后调用excludedRefs
方法添加白名单,在AndroidExcludedRefs
枚举类中定义了忽略列表信息,如果这些列表中的类发生了内存泄露,并不会显示出来,同时HeapAnalyzer
计算GCRoot强引用路径,也会忽略这些类,如果你希望自己项目中某个类泄露的,但是不希望他显示,就可以把类添加到这个上面
public enum AndroidExcludedRefs {
// ######## Android SDK Excluded refs ########
ACTIVITY_CLIENT_RECORD__NEXT_IDLE(SDK_INT >= KITKAT && SDK_INT <= LOLLIPOP) {
@Override void add(ExcludedRefs.Builder excluded) {
excluded.instanceField(“android.app.ActivityThread$ActivityClientRecord”, “nextIdle”)
.reason(“Android AOSP sometimes keeps a reference to a destroyed activity as a”
- " nextIdle client record in the android.app.ActivityThread.mActivities map."
- " Not sure what’s going on there, input welcome.");
}
}
…
}
最后调用了buildAndInstall
方法,创建了一个RefWatcher
对象并返回,这个对象是用于检测是否有对象未被回收导致的内存泄露
/**
- Creates a {@link RefWatcher} instance and starts watching activity references (on ICS+).
*/
public RefWatcher buildAndInstall() {
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
LeakCanary.enableDisplayLeakActivity(context);
ActivityRefWatcher.install((Application) context, refWatcher);
}
return refWatcher;
}
因为分析泄露是在另一个进程进行的,所以判断当前启动的Application是否在分析内存泄露的进程中,如果是就直接返回DISABLED
,不在进行后续初始化,如果发现是在程序主进程中,就进行初始化
LeakCanary.enableDisplayLeakActivity(context);
主要作用是调用PackageManager
将DisplayLeakActivity
设置为可用。
public static void enableDisplayLeakActivity(Context context) {
setEnabled(context, DisplayLeakActivity.class, true);
}
public static void setEnabled(Context context, final Class<?> componentClass,
final boolean enabled) {
final Context appContext = context.getApplicationContext();
executeOnFileIoThread(new Runnable() {
@Override public void run() {
setEnabledBlocking(appContext, componentClass, enabled);
}
});
}
public static void setEnabledBlocking(Context appContext, Class<?> componentClass,
boolean enabled) {
ComponentName component = new ComponentName(appContext, componentClass);
PackageManager packageManager = appContext.getPackageManager();
int newState = enabled ? COMPONENT_ENABLED_STATE_ENABLED : COMPONENT_ENABLED_STATE_DISABLED;
// Blocks on IPC.
packageManager.setComponentEnabledSetting(component, newState, DONT_KILL_APP);
}
从配置文件看LeakCanary这几个文件都是运行在新进程的,DisplayLeakActivity
默认enable=false
,这样就可以一开始隐藏启动图标
接着 ActivityRefWatcher.install((Application) context, refWatcher);
这里把refWatcher
当做参数传入,同时对Activity的生命周期进行了监听
public static void install(Application application, RefWatcher refWatcher) {
new ActivityRefWatcher(application, refWatcher).watchActivities();
}
public void watchActivities() {
// Make sure you don’t get installed twice.
stopWatchingActivities();
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}
public void stopWatchingActivities() {
application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks);
}
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new Application.ActivityLifecycleCallbacks() {
@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override public void onActivityStarted(Activity activity) {
}
@Override public void onActivityResumed(Activity activity) {
}
@Override public void onActivityPaused(Activity activity) {
}
@Override public void onActivityStopped(Activity activity) {
}
@Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override public void onActivityDestroyed(Activity activity) {
ActivityRefWatcher.this.onActivityDestroyed(activity);
}
};
void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}
首先就是注册Activity的生命周期的监听器lifecycleCallbacks
,这个监听器可以监听项目中所有Activity的的生命周期,然后在Activity销毁调用onActivityDestroyed
的时候,LeakCanary就会获取这个Activity,然后对其进行分析,看是否有内存泄露
分析内存泄露
这里分析对象是否内存泄露的是RefWatcher
类,下面简单介绍一下这个类的成员变量
- WatchExecutor watchExecutor:确保任务在主线程进行,同时默认延迟5s执行任务,留时间给系统GC
- DebuggerControl debuggerControl:控制中心
- GcTrigger gcTrigger:内部调用
Runtime.getRuntime().gc()
,手动触发GC - HeapDumper heapDumper:用于创建.hprof文件,用于储存head堆快照,可以知道哪些程序在大部分使用内存
- HeapDump.Listener heapdumpListener:分析结果完成后会告诉这个监听器
- ExcludedRefs excludedRefs:分析内存泄露的白名单
从上面可以看出,每当Activity销毁,就会调用RefWatcher
的watch
方法,去分析是否是内存泄露
public void watch(Object watchedReference) {
watch(watchedReference, “”);
}
public void watch(Object watchedReference, String referenceName) {
if (this == DISABLED) {
return;
}
checkNotNull(watchedReference, “watchedReference”);
checkNotNull(referenceName, “referenceName”);
final long watchStartNanoTime = System.nanoTime();
String key = UUID.randomUUID().toString();
retainedKeys.add(key);
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
ensureGoneAsync(watchStartNanoTime, reference);
}
上面代码主要作用是,先生成一个随机数key放在retainedKeys
容器里,用来区分对象是否被回收,创建了一个弱引用,然后把要分析的Activity对象存入,然后调用了ensureGoneAsync
方法
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
watchExecutor.execute(new Retryable() {
@Override public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);
}
});
}
然后用watchExecutor
去调度分析任务,这个主要是保证,在主线程进行,延迟5s,让系统有时间GC
@SuppressWarnings(“ReferenceEquality”) // Explicitly checking for named null.
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
long gcStartNanoTime = System.nanoTime();
long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
removeWeaklyReachableReferences();
if (debuggerControl.isDebuggerAttached()) {
// The debugger can create false leaks.
return RETRY;
}
if (gone(reference)) {
return DONE;
}
gcTrigger.runGc();
removeWeaklyReachableReferences();
if (!gone(reference)) {
long startDumpHeap = System.nanoTime();
long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
File heapDumpFile = heapDumper.dumpHeap();
if (heapDumpFile == RETRY_LATER) {
// Could not dump the heap.
return RETRY;
}
long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
heapdumpListener.analyze(
new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
gcDurationMs, heapDumpDurationMs));
}
return DONE;
}
private void removeWeaklyReachableReferences() {
// WeakReferences are enqueued as soon as the object to which they point to becomes weakly
// reachable. This is before finalization or garbage collection has actually happened.
KeyedWeakReference ref;
while ((ref = (KeyedWeakReference) queue.poll()) != null) {
retainedKeys.remove(ref.key);
}
}
private boolean gone(KeyedWeakReference reference) {
return !retainedKeys.contains(reference.key);
}
首先通过removeWeaklyReachableReferences()
方法,尝试从弱引用队列获取待分析对象,如果不为空说明被系统回收了,就把retainedKeys
中的key值移除,如果被系统回收直接返回DONE,如果没有被系统回收,就手动调用 gcTrigger.runGc();
手动触发系统gc,然后再次调用removeWeaklyReachableReferences()
方法,如过还是为空,则判断为内存泄露
GcTrigger DEFAULT = new GcTrigger() {
@Override public void runGc() {
// Code taken from AOSP FinalizationTest:
// https://android.googlesource.com/platform/libcore/+/master/support/src/test/java/libcore/
// java/lang/ref/FinalizationTester.java
// System.gc() does not garbage collect every time. Runtime.gc() is
// more likely to perfom a gc.
Runtime.getRuntime().gc();
enqueueReferences();
System.runFinalization();
}
private void enqueueReferences() {
// Hack. We don’t have a programmatic way to wait for the reference queue daemon to move
// references to the appropriate queues.
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new AssertionError();
}
}
文末
很多人在刚接触这个行业的时候或者是在遇到瓶颈期的时候,总会遇到一些问题,比如学了一段时间感觉没有方向感,不知道该从那里入手去学习,对此我整理了一些资料,需要的可以免费分享给大家
这里笔者分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司2021年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。
【视频教程】
天道酬勤,只要你想,大厂offer并不是遥不可及!希望本篇文章能为你带来帮助,如果有问题,请在评论区留言。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
7868338)]
[外链图片转存中…(img-ZOXGxPiy-1715727868338)]
【视频教程】
[外链图片转存中…(img-v4BFt5o0-1715727868339)]
天道酬勤,只要你想,大厂offer并不是遥不可及!希望本篇文章能为你带来帮助,如果有问题,请在评论区留言。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!