目录
一、内存分配
了解java内存的分配原理对分析内存有非常大帮助,特别是静态内存部分啦。
Java和c++的内存分配情况好比有一堵内存动态分配和垃圾收集技术围成的“高墙”,墙外面的人想进去(c++),墙里面的人想出来(java)。
c++是对象实例没有指针指向它,被孤立,导致内存泄露,java是没用的对象实例还有多余的引用,GC不能回收。
JVM内存运行图

共享区域
方法区( Method Area):存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译编译后的代码。
堆(Heap):存放java几乎所有对象实例 ,JIT除外。如果对象需要的内存不够,OutOfMemoryError异常。
隔离区域
虚拟机栈(VM Stack):方法执行的内存模型,用于存储方法入口、局部变量表、操作数栈、动态连接。如果栈深度大于虚拟机的栈深,抛出StackOverflowError异常,如果对象实例对象需要的内存不够,抛出OutOfMemoryError异常。
本地方法栈(Native Method Stack): 和VM Static 差不多的作用。只是虚拟机是native方法的内存模型(c/c++方法)。
程序计数器(Program Counter Register):存放线程的行号指示器,每个线程都有自己的独立的计数器。
android虚拟机


二、GG过程
长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄漏,尽管短生命周期对象已经不再需要,但是因为长生命周期持有它的引用而导致不能被回收,这就是Java中内存泄漏的发生场景。

对于不同的语言平台来说,进行标记回收内存的算法是不一样的,像 Android(Java)则采用 GC-Root 的标记回收算法(Mark sweap)。下面这张图就展示了 Android 内存的回收管理策略(图来自Google 2011的IO大会)
图中的每个圆节点代表对象的内存资源,箭头代表可达路径。当圆节点与 GC Roots 存在可达路径时,表示当前资源正被引用,虚拟机是无法对其进行回收的(如图中的黄色节点)。反过来,如果圆节点与 GC Roots 不存在可达路径,则意味着这块对象的内存资源不再被程序引用,系统虚拟机可以在 GC 过程中将其回收掉。
深入JVM虚拟机(三) Java GC垃圾收集 https://blog.csdn.net/yuan_xw/article/details/51434007
三、GC算法
1.引用计数法

2、标记清除法

4、复制算法
5、分代思想:
四、常见内存泄漏原因
1.非静态内部类的静态实例
public class SecondActivity extends AppCompatActivity {
private static Object inner;
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button) findViewById(R.id.bt_next);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
createInnerClass();
finish();
}
});
}
void createInnerClass() {
class InnerClass {
}
inner = new InnerClass();//1
}
}
不要使用
2.匿名内部类的静态实例
public class AsyncTaskActivity extends AppCompatActivity {
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_async_task);
button = (Button) findViewById(R.id.bt_next);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startAsyncTask();
finish();
}
});
}
void startAsyncTask() {
new AsyncTask<Void, Void, Void>() {//1
@Override
protected Void doInBackground(Void... params) {
while (true) ;
}
}.execute();
}
}
改
public class AsyncTaskActivity extends AppCompatActivity {
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_async_task);
button = (Button) findViewById(R.id.bt_next);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startAsyncTask();
finish();
}
});
}
void startAsyncTask() {
new MyAsyncTask().execute();
}
private static class MyAsyncTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... params) {
while (true) ;
}
}
}
application service activity 的调用地方

3 Handler内存泄漏
Handler的Message被存储在MessageQueue中,有些Message并不能马上被处理,它们在MessageQueue中存在的时间会很长,这就会导致Handler无法被回收。如果Handler 是非静态的,则Handler也会导致引用它的Activity或者Service不能被回收。
public class HandlerActivity extends AppCompatActivity {
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
button = (Button) findViewById(R.id.bt_next);
final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mHandler.sendMessageDelayed(Message.obtain(), 60000);
finish();
}
});
}
}
改
解决方法就是要使用一个静态的Handler内部类,Handler持有的对象要使用弱引用,并且在Activity的Destroy方法中移除MessageQueue中的消息
public class HandlerActivity extends AppCompatActivity {
private Button button;
private MyHandler myHandler = new MyHandler(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
button = (Button) findViewById(R.id.bt_next);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
myHandler.sendMessageDelayed(Message.obtain(), 60000);
finish();
}
});
}
public void show() {
}
private static class MyHandler extends Handler {
private final WeakReference<HandlerActivity> mActivity;
public MyHandler(HandlerActivity activity) {
mActivity = new WeakReference<HandlerActivity2>(activity);
}
@Override
public void handleMessage(Message msg) {
if (mActivity != null && mActivity.get() == null) {
mActivity.get().show();
}
}
}
@Override
public void onDestroy() {
if (myHandler != null) {
myHandler.removeCallbacksAndMessages(null);
}
super.onDestroy();
}
}
3.各种监听没有释放
自己添加的监听记得及时去除监听
4.单例类持有外部的引用 Activity Context
public class AppSettings {
private Context mAppContext;
private static AppSettings mAppSettings = new AppSettings();
public static AppSettings getInstance() {
return mAppSettings;
}
public final void setup(Context context) {
mAppContext = context;
}
}
对于不是必须使用Activity Context的情况(Dialog的Context就必须是Activity Context),我们可以考虑使用Application Context来代替Activity的Context,这样可以避免Activity泄露,
改
public final void setup(Context context) {
mAppContext = context.getApplicationContext();
}
5 静态View
使用静态View可以避免每次启动Activity都去读取并渲染View,但是静态View会持有Activity的引用,导致Activity无法被回收,解决的办法就是在onDestory方法中将静态View置为null。
6 WebView
通常的解决办法就是为WebView单开一个进程,使用AIDL与应用的主进程进行通信。WebView进程可以根据业务需求,在合适的时机进行销毁
7 资源对象未关闭
资源对象比如Cursor、File等,往往都用了缓冲,不使用的时候应该关闭它们。把他们的引用置为null,而不关闭它们,往往会造成内存泄漏。因此,在资源对象不使用时,一定要确保它已经关闭,通常在finally语句中关闭,防止出现异常时,资源未被释放的问题。
8 集合中对象没清理
通常把一些对象的引用加入到了集合中,当不需要该对象时,如果没有把它的引用从集合中清理掉,这样这个集合就会越来越大。如果这个集合是static的话,那情况就会更加严重。
9 Bitmap对象
临时创建的某个相对比较大的bitmap对象,在经过变换得到新的bitmap对象之后,应该尽快回收原始的bitmap,这样能够更快释放原始bitmap所占用的空间。
避免静态变量持有比较大的bitmap对象或者其他大的数据对象,如果已经持有,要尽快置空该静态变量。
http://liuwangshu.cn/application/performance/ram-3-memory-leak.html
五、内存泄漏的的分析
1.使用 adb shell dumpsys meminfo [PackageName],可以打印出指定包名的应用内存信息。它是最简单直接反应是否哟内存泄漏的工具。
主要查看
total memory:一般退出activity都已有内存的变化,回收;
java heap:java对象的分配情况;
Activities:当前存活的activity,如果有多个activity,当心有内存泄漏!
localhost:jdk7u-dev li$ adb shell dumpsys meminfo cn.xxxx.android
Applications Memory Usage (in Kilobytes):
Uptime: 188198513 Realtime: 458787859
** MEMINFO in pid 26780 [cn.xxxx.android] **
Pss Private Private SwapPss Heap Heap Heap
Total Dirty Clean Dirty Size Alloc Free
------ ------ ------ ------ ------ ------ ------
Native Heap 16970 16776 156 0 32128 14411 17716
Dalvik Heap 42663 38076 4448 0 55728 39344 16384
Dalvik Other 8273 8264 0 0
Stack 1816 1816 0 0
Ashmem 238 120 0 0
Gfx dev 21244 21244 0 0
Other dev 70 0 68 0
.so mmap 8305 460 5232 98
.jar mmap 4 0 0 0
.apk mmap 34883 22316 12196 0
.ttf mmap 603 0 188 0
.dex mmap 7153 6260 772 0
.oat mmap 5801 0 648 0
.art mmap 4331 2096 972 3
Other mmap 766 4 684 1
EGL mtrack 11152 11152 0 0
GL mtrack 5576 5576 0 0
Unknown 7891 7888 0 6
TOTAL 177847 142048 25364 108 87856 53755 34100
App Summary
Pss(KB)
------
Java Heap: 41144
Native Heap: 16776
Code: 48072
Stack: 1816
Graphics: 37972
Private Other: 21632
System: 10435
TOTAL: 177847 TOTAL SWAP PSS: 108
Objects
Views: 224 ViewRootImpl: 4
AppContexts: 5 Activities: 1
Assets: 18 AssetManagers: 3
Local Binders: 100 Proxy Binders: 38
Parcel memory: 24 Parcel count: 96
Death Recipients: 4 OpenSSL Sockets: 0
WebViews: 0
SQL
MEMORY_USED: 369
PAGECACHE_OVERFLOW: 105 MALLOC_SIZE: 62
DATABASES
pgsz dbsz Lookaside(b) cache Dbname
4 24 61 11/40/7 /data/user/0/cn.xxxx.android/databases/babyfs
1 3 0/0/0 (attached) temp
4 24 72 591/143/8 /data/user/0/cn.xxxx.android/databases/statistic
1 3 0/0/0 (attached) temp
4 20 48 12/30/9 /data/user/0/cn.xxxx.android/databases/tls_sdk.db
Asset Allocations
zip:/data/app/cn.xxxx.android-1/base.apk:/assets/ca.crt: 1K
2.使用 adb shell dumpsys activity [PackageName],可以打印出指定包名的应用acitivity信息
3.Android studio profiler
利用堆转储查看当前没有被回收的activity引用情况,对可疑的引用进行修改。解决activity不能回收问题。
Heap类型分为:
App Heap – 当前App使用的Heap
Image Heap – 磁盘上当前App的内存映射拷贝
Zygote Heap – Zygote进程Heap(每个App进程都是从Zygote孵化出来的, 这部分基本是framework中的通用的类的Heap)
Class List View – 类列表方式
Package Tree View – 根据包结构的树状显示
紧接着下面的表名:
列名 说明
Class Name Heap中的所有Class
Total Count 内存中该类这个对象总共的数量
Heap Count 堆内存中这个类对象的个数
Sizeof 每个该实例占用的内存大小
Shallow Size 所有该类的实例占用的内存大小
Retained Size 所有该类对象被释放掉,会释放多少内存
接下来是B区域
列名 说明
Instance 该类的实例
Depth 深度, 从任一GC Root点到该实例的最短跳数
Dominating Size 该实例可支配的内存大小
C区域则描述的是B中实例具体被引用信息。