内存泄漏和内存溢出你分的清谁是谁么?
Java不像C/C++自己掌握对象的释放,一般都是jvm包管了而内存泄露是怎么发生的呢
我们先了解一下java内存结构是怎么样的呢?
Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域都有各自的用途,以及创建和销毁的时
间,有的区域随着虚拟机进程的启动而存在,有些区域则是依赖用户线程的启动和结束而建立和销毁。
我们先看看jvm内存结构吧
1. java运行时数据区域
1.1 程序计数器:
是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器
1.2 Java虚拟机栈:
每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口
等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
1.3 本地方法栈
与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务
1.4 Java堆
java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内
存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存
1.5 方法区
方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等
数据。
想看详细的Java内存管理:深入Java内存区域
看完这个内存结构,其实你有不有发现,基本上jvm会帮你进行自动GC,但是一旦有引用存在,内存是无法被GC掉的,而且只有一块区域不会发生内存溢出异常-程序计数器
如果你对jvm内存垃圾回收有兴趣,可以看看我之前写的
Java基础之GC垃圾回收的历史
2. 内存泄露
该被释放的资源,被某个对象所持有不能释放,导致的
2.1 主要引起内存泄露有哪些呢
- 单例
- 匿名内部类
- 资源未关闭
如:
- Handler 引起的内存泄漏,非静态内部类,会持有外部引用,导致activity无法销毁(非静态匿名内部类对象它会自动持有外部类Activity的引用,从而导致SecondActivity无法被回收,造成内存泄漏)
- 单例模式引起的内存泄漏(静态类持有了外部Context引用导致Activity无法销毁)
- 非静态内部类创建静态实例引起的内存泄漏(非静态的内部类也会持有外部引用,同时又有静态实例引用Activity,导致无法销毁)
- 非静态匿名内部类引起的内存泄漏
- 注册/反注册未成对使用引起的内存泄漏
- 资源对象没有关闭引起的内存泄漏
- 集合对象没有及时清理引起的内存泄漏
2.2 检查内存泄露的工具
- Monitor工具
- leakcanary
2.3 内存泄露的总结
其实工具都不咋好使,主要还是得靠开发人员平时多注意一下开发规范,如内部类和静态,谨慎使用,同时对于new出来的东西要自己多注意想一下什么时候会销毁,需不需要自己主动销毁一下
3. 内存溢出
当前占用的内存加上我们申请的内存资源超过了虚拟机的最分配的进程最大限制就会抛出的异常
3.1 主要体现在bitmap
解决办法:
- 图片显示,可以使用缩略图,不用加载原图
- 及时释放内存,使用完就立即释放
- 图片压缩,可以使用ARGB_4444色彩质量的,也可以根据手机像素来压缩
- inbitmap属性,3.0+的bitmap属性,支持相同尺寸的复用
- 捕获异常
但其实内存溢出异常也是可以捕捉的
Bitmap bitmap = null; try { // 实例化Bitmap bitmap = BitmapFactory.decodeFile(path); } catch (OutOfMemoryError e) { // } if (bitmap == null) { // 如果实例化失败 返回默认的Bitmap对象 return defaultBitmapMap; }
3.2 关于其他的会内存溢出的地方
- listview优化
- 避免在ondraw中执行创建对象方法
- 谨慎使用多进程
3.3 内存溢出的总结
主要还是注意使用bitmap,毕竟这货分分钟就能溢出,就是还有注意的会重复多次调用的方法不要存在创建对象的代码!