@Test
public void onPhantomReference()throws InterruptedException{
String str = new String(“123456”);
ReferenceQueue queue = new ReferenceQueue();
// 创建虚引用,要求必须与一个引用队列关联
PhantomReference pr = new PhantomReference(str, queue);
System.out.println(“PhantomReference:” + pr.get());
System.out.printf(“ReferenceQueue:” + queue.poll());
}
虚引用顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于: 虚引用必须和引用队列 (ReferenceQueue) 联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。
| 引用类型 | 调用方式 | GC | 是否内存泄漏 |
| — | — | — | — |
| 强引用 | 直接调用 | 不回收 | 是 |
| 软引用 | .get() | 视内存情况回收 | 否 |
| 弱引用 | .get() | 回收 | 不可能 |
| 虚引用 | null | 任何时候都可能被回收,相当于没有引用一样 | 否 |
分析内存常用工具
工具很多,掌握原理方法,工具随意挑选使用。
内存泄漏
产生的原因: 一个长生命周期的对象持有一个短生命周期对象的引用,通俗点讲就是该回收的对象,因为引用问题没有被回收,最终会产生 OOM。
下面我们来利用 Profile 来检查项目是否有内存泄漏
Allocations: 动态分配对象个数
Deallocation: 解除分配的对象个数
Total count: 对象的总数
Shalow Size: 对象本身占用的内存大小
Retained Size: GC 回收能收走的内存大小
将 export 导出的 dprof 文件转换为 Mat 的 dprof 文件
cd /d 进入到 Android sdk/platform-tools/hprof-conv.exe
//转换命令 hprof-conv -z src des
D:\Android\AndroidDeveloper-sdk\android-sdk-windows\platform-tools>hprof-conv -z D:\temp_\temp_6.hprof D:\temp_\memory6.hprof
下载 Mat 工具
打开 MemoryAnalyzer.exe 点击左上角 File 菜单中的 Open Heap Dupm
这里我们得知是一个 ilsLoginListener 引用了 LoginView,我们来看下代码最后怎么解决的。
代码中我们找到了 LoginView 这个类,发现是一个单例中的回调引起的内存泄漏,下面怎么解决勒,请看第七小点。
2种解决单例中的内存泄漏
将引用置为 null
/**
*/
public void unRemoveRegisterListener(){
mMessageController.unBindListener();
}
public void unBindListener(){
if (listener != null){
listener = null;
}
}
//将监听器放入弱引用中
WeakReference listenerWeakReference = new WeakReference<>(listener);
//从弱引用中取出回调
listenerWeakReference.get();
示例 :
public class AppManager {
private static AppManager sInstance;
private CallBack mCallBack;
private Context mContext;
private AppManager(Context context) {
this.mContext = context;
}
public static AppManager getInstance(Context context) {
if (sInstance == null) {
sInstance = new AppManager(context);
}
return sInstance;
}
public void addCallBack(CallBack call){
mCallBack = call;
}
}
以 Activity 为例,当 Activity 调用 getInstance 传入 this ,那么 sInstance 就会持有 Activity 的引用,当 Activity 需要关闭的时候需要 回收的时候,发现 sInstance 还持有 没有用的 Activity 引用,导致 Activity 无法被 GC 回收,就会造成内存泄漏
//在 Activity 中实现单例的回调
AppManager.getInstance(getAppcationContext()).addCallBack(new CallBack(){
@Override
public void onStart(){
}
});
这里的 new CallBack() 匿名内部类 默认持有外部的引用,造成 CallBack 释放不了,那么怎么解决了,请看下面解决方法
解决方法:
getInstance(Context context) context 都传入 Appcation 级别的 Context,或者实在是需要传入 Activity 的引用就用 WeakReference 这种形式。
匿名内部类建议大家单独写一个文件或者
public void addCallBack(CallBack call){
WeakReference mCallBack= new WeakReference(call);
}
示例:
//在 Activity 中实现 Handler
class MyHandler extends Handler{
private Activity m;
public MyHandler(Activity activity){
m=activity;
}
// class…
}
这里的 MyHandler 持有 activity 的引用,当 Activity 销毁的时候,导致 GC 不会回收造成 内存泄漏。
解决方法:
1.使用静态内部类 + 弱引用
2.在 Activity onDestoty() 中处理 removeCallbacksAndMessages()
@Override
protected void onDestroy() {
super.onDestroy();
if(null != handler){
handler.removeCallbacksAndMessages(null);
handler = null;
}
}
示例:
public class MainActivity extends AppCompatActivity {
private static Police sPolice;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (sPolice != null) {
sPolice = new Police(this);
}
}
}
class Police {
public Police(Activity activity) {
}
}
这里 Police 持有 activity 的引用,会造成 activity 得不到释放,导致内存泄漏。
解决方法:
//1. sPolice 在 onDestory()中 sPolice = null;
//2. 在 Police 构造函数中 将强引用 to 弱引用;
参考 第二点 Handler 的处理方式
示例:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(){
@Override
public void run() {
super.run();
}
};
}
}
很多初学者都会像上面这样新建线程和异步任务,殊不知这样的写法非常地不友好,这种方式新建的子线程Thread
和AsyncTask
都是匿名内部类对象,默认就隐式的持有外部Activity
的引用,导致Activity
内存泄露。
解决方法:
//静态内部类 + 弱引用
//单独写一个文件 + onDestory = null;
示例:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
registerReceiver(mReceiver, new IntentFilter());
}
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// TODO ------
}
};
}
在注册观察则模式的时候,如果不及时取消也会造成内存泄露。比如使用Retrofit + RxJava
注册网络请求的观察者回调,同样作为匿名内部类持有外部引用,所以需要记得在不用或者销毁的时候取消注册。
解决方法:
//Activity 中实现 onDestory()反注册广播得到释放
@Override
protected void onDestroy() {
super.onDestroy();
this.unregisterReceiver(mReceiver);
}
示例:
public class MainActivity extends AppCompatActivity {
/*模拟计数/
private int mCount = 1;
private Timer mTimer;
private TimerTask mTimerTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
mTimer.schedule(mTimerTask, 1000, 1000);
}
private void init() {
mTimer = new Timer();
mTimerTask = new TimerTask() {
@Override
public void run() {
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
addCount();
}
});
}
};
}
private void addCount() {
mCount += 1;
}
}
当我们Activity
销毁的时,有可能Timer
还在继续等待执行TimerTask
,它持有Activity 的引用不能被 GC 回收,因此当我们 Activity 销毁的时候要立即cancel
掉Timer
和TimerTask
,以避免发生内存泄漏。
解决方法:
//当 Activity 关闭的时候,停止一切正在进行中的定时任务,避免造成内存泄漏。
private void stopTimer() {
if (mTimer != null) {
mTimer.cancel();
mTimer = null;
}
if (mTimerTask != null) {
mTimerTask.cancel();
mTimerTask = null;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
stopTimer();
}
示例:
ArrayList,HashMap,IO,File,SqLite,Cursor 等资源用完一定要记得 clear remove 等关闭一系列对资源的操作。
解决方法:
用完即刻销毁
示例:
动画同样是一个耗时任务,比如在 Activity 中启动了属性动画 (ObjectAnimator) ,但是在销毁的时候,没有调用 cancle 方法,虽然我们看不到动画了,但是这个动画依然会不断地播放下去,动画引用所在的控件,所在的控件引用 Activity ,这就造成 Activity 无法正常释放。因此同样要在Activity 销毁的时候 cancel 掉属性动画,避免发生内存泄漏。
解决方法:
@Override
protected void onDestroy() {
super.onDestroy();
//当关闭 Activity 的时候记得关闭动画的操作
mAnimator.cancel();
}
示例:
//如果在开发调试中遇见 Android 源码或者 第三方 SDK 持有了我们当前的 Activity 或者其它类,那么现在怎么办了。
解决方法:
//当前是通过 Java 中的反射找到某个类或者成员,来进行手动 = null 的操作。
内存抖动
内存频繁的分配与回收,(分配速度大于回收速度时) 最终产生 OOM 。
也许下面的录屏更能解释什么是内存抖动
可以看出当我点击了一下 Button 内存就频繁的创建并回收(注意看垃圾桶)。
那么我们找出代码中具体那一块出现问题了勒,请看下面一段录屏
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
imPrettySureSortingIsFree();
}
});
/**
* 排序后打印二维数组,一行行打印
*/
public void imPrettySureSortingIsFree() {
int dimension = 300;
int[][] lotsOfInts = new int[dimension][dimension];
Random randomGenerator = new Random();
for (int i = 0; i < lotsOfInts.length; i++) {
for (int j = 0; j < lotsOfInts[i].length; j++) {
lotsOfInts[i][j] = randomGenerator.nextInt();
}
}
for (int i = 0; i < lotsOfInts.length; i++) {
String rowAsStr = “”;
//排序
int[] sorted = getSorted(lotsOfInts[i]);
//拼接打印
for (int j = 0; j < lotsOfInts[i].length; j++) {
rowAsStr += sorted[j];
if (j < (lotsOfInts[i].length - 1)) {
rowAsStr += ", ";
}
}
在当下这个信息共享的时代,很多资源都可以在网络上找到,只取决于你愿不愿意找或是找的方法对不对了
很多朋友不是没有资料,大多都是有几十上百个G,但是杂乱无章,不知道怎么看从哪看起,甚至是看后就忘
如果大家觉得自己在网上找的资料非常杂乱、不成体系的话,我也分享一套给大家,比较系统,我平常自己也会经常研读。
2021最新上万页的大厂面试真题
七大模块学习资料:如NDK模块开发、Android框架体系架构…
只有系统,有方向的学习,才能在段时间内迅速提高自己的技术。
这份体系学习笔记,适应人群:
第一,学习知识比较碎片化,没有合理的学习路线与进阶方向。
第二,开发几年,不知道如何进阶更进一步,比较迷茫。
第三,到了合适的年纪,后续不知道该如何发展,转型管理,还是加强技术研究。如果你有需要,我这里恰好有为什么,不来领取!说不定能改变你现在的状态呢!
由于文章内容比较多,篇幅不允许,部分未展示内容以截图方式展示 。
om();
for (int i = 0; i < lotsOfInts.length; i++) {
for (int j = 0; j < lotsOfInts[i].length; j++) {
lotsOfInts[i][j] = randomGenerator.nextInt();
}
}
for (int i = 0; i < lotsOfInts.length; i++) {
String rowAsStr = “”;
//排序
int[] sorted = getSorted(lotsOfInts[i]);
//拼接打印
for (int j = 0; j < lotsOfInts[i].length; j++) {
rowAsStr += sorted[j];
if (j < (lotsOfInts[i].length - 1)) {
rowAsStr += ", ";
}
}
在当下这个信息共享的时代,很多资源都可以在网络上找到,只取决于你愿不愿意找或是找的方法对不对了
很多朋友不是没有资料,大多都是有几十上百个G,但是杂乱无章,不知道怎么看从哪看起,甚至是看后就忘
如果大家觉得自己在网上找的资料非常杂乱、不成体系的话,我也分享一套给大家,比较系统,我平常自己也会经常研读。
2021最新上万页的大厂面试真题
[外链图片转存中…(img-7pPx9vkZ-1647525823310)]
七大模块学习资料:如NDK模块开发、Android框架体系架构…
[外链图片转存中…(img-855u5iEX-1647525823311)]
只有系统,有方向的学习,才能在段时间内迅速提高自己的技术。
这份体系学习笔记,适应人群:
第一,学习知识比较碎片化,没有合理的学习路线与进阶方向。
第二,开发几年,不知道如何进阶更进一步,比较迷茫。
第三,到了合适的年纪,后续不知道该如何发展,转型管理,还是加强技术研究。如果你有需要,我这里恰好有为什么,不来领取!说不定能改变你现在的状态呢!
由于文章内容比较多,篇幅不允许,部分未展示内容以截图方式展示 。