美文网首页
为什么会发生内存泄漏

为什么会发生内存泄漏

作者: 程序设计法师 | 来源:发表于2019-04-12 17:00 被阅读0次

Jvm会根据generation(代)来进行垃圾回收(GC)一共被分为young generation(年轻代)、tenured generation(老年代)、permanent generation(永久代,perm gen)perm gen(或称Non-Heap非堆)是个异类,注意:heap空间不包括perm gen

绝大多数的对象都在young generation中被分配,也在年轻代中被收回,当年轻代的空间被填满时,GC会进行minor collection(次回收),次回收不涉及到heap中的其他generation,minor collection会根据weak generational hypothesis(弱年代假设)来假设young generation中的大量的对象都是垃圾需要回收,minor collection的过程会非常快,在young generation中没有回收的对象被转移到tenured generation,然而tenured generation也会被填满,最终触发major collection(主回收),这次回收针对整个heap,由于涉及到大量对象,所以比minor collection慢得多

什么样的对象GC才会回收呢,当然是GC发现通过任何引用链都无法访问某个对象的时候,该对象即被回收,GCRoots就是分析这一过程的起点,JVM就是GC Roots,所以GCRoots就是这样在内存中保持对象可到达性的,一旦不可到达,即被回收,通常GC Roots是一个在current thread的调用栈上的对象(如方法参数和局部变量)或者是线程自身或者是系统类加载器加载的类,以及本地代码保留的活动对象,所以GCRoots是分析对象为何还存在于内存中的利器

不同引用级别反映了对象的生命周期
强引用,软引用(内存吃紧才回收对象,一般用来实现缓存),弱引用,虚引用

  • shallow size 是指对象本身占用内存大小
  • retained size 是指对象自己的shallow size加上从该对象能直接或间接访问到对象的shallow size之和
    我们可以利用DDMS工具导出hprof文件并将其导入MAT工具,分析内存泄漏
    还可以在代码中按钮点击事件中手动生成
public void onClick(View view){
android.os.Debug.dumpHprofData(“data/temp/myapp.hprof”);
}

生成hprof文件之后还需要转换,将.hprof文件复制到/android_sdk/tools目录下并输入命令“hprofconv xxx.hprof yyy.hprof”,然后用MAT工具导入生成后的文件即可

用top命令查看某个进程的内存,创建脚本文件,该文件的内存是指定程序每隔一秒输出某个进程的内存使用情况
#!/bin/bash
while true;do
adb shell procrank | grep "com.android.music"
sleep 1
done

配合使用procank工具,可以查看music进程每一秒内存的使用情况

使用top命令
adb shell top -m 10 //查看使用资源最多的10个进程
adb shell top | grep com.android.music 查看music进程的内存
使用free来显示内存的使用情况
free -b -k -m -o -s delay -t -v 

-b -k -m 分别表示以字节kb,MB为单位显示内存的使用情况
-s delay 显示每隔多少秒来显示一次内存使用情况
-t 显示内存总和列
-o 不显示缓冲区调节列

安卓每个进程都在自己的虚拟机中运行,如果进程被杀掉,不会影响其他进程,Android为应用进程分配的内存上限保存在“ANDROID_SOURCE/system/core/rootdir/init.rc”脚本中

几种发生内存泄漏的情况

1 查询数据库忘记关闭游标
2 没有及时释放对象的引用
3 Bitmap没有recycle()
4 listView不习惯使用缓存的convertView

public View getView(int position,View convertView,ViewGroup parent){
View view=new XXX();
....
return view;
}

优化后

public View getView(int position,View convertView,ViewGroup parent){
View view=null;
if(convertView!=null){
    view=convertView;
    populate(view,getItem(postion));
}else{
    view =new XXX();
}
....
    return view;
}

我们可以优化Dalvik虚拟机的堆内存分配,手动干涉GC处理,使用类dalvik.system.VMRuntime提供的方法setTargetHeapUtilizaton可以增强程序堆内存的处理效率

private final static float TARGET_HEAP_UTILIZATION=0.75F;
VMRuntime.getRuntime().setTargetHeapUtilization(TARGET_HEAP_UTILIZATION);
//还可以用如下方法定义堆内存的大小,优化功能
private final static int CWJ_HEAP_SIZE=6*1024*1024;
VMRuntime.getRuntime().setMinimumHeapSize(CWJ_HEAP_SIZE);
//设置最小heap内存为6MB大小

堆(HEAP)是VM中占用内存最多的部分,通常是动态分配的。堆的大小不是一成不变的,通常有一个分配机制来控制它的大小。比如初始的HEAP是4M大,当4M的空间被占用超过75%的时候,重新分配堆为8M大;当8M被占用超过75%,分配堆为16M大。倒过来,当16M的堆利用不足30%的时候,缩减它的大小为8M大。重新设置堆的大小,尤其是压缩,一般会涉及到内存的拷贝,所以变更堆的大小对效率有不良影响

上面只是个例子,不过可以看到三个参数:max heap size, min heap size, heap utilization(堆利用率)
Max Heap Size,是堆内存的上限值,Android的缺省值是16M(某些机型是24M),对于普通应用这是不能改的。函数setMinimumHeapSize其实只是改变了堆的下限值,它可以防止过于频繁的堆内存分配,当设置最小堆内存大小超过上限值时仍然采用堆的上限值,对于内存不足没什么作用

setTargetHeapUtilization(float newTarget) 可以设定内存利用率的百分比,当实际的利用率偏离这个百分比的时候,虚拟机会在GC的时候调整堆内存大小,让实际占用率向个百分比靠拢

相关文章

网友评论

      本文标题:为什么会发生内存泄漏

      本文链接:https://www.haomeiwen.com/subject/xtzcwqtx.html