![](https://img.haomeiwen.com/i11572843/e97c682aa18d2e15.jpg)
要做内存优化,首先我们要了解一下我们写的代码,在java虚拟机中是怎样分配内存的,static final 变量,局部变量,对象实例,等等,他们在JVM中如何使用内存以及何时被回收,以及GC是如何回收的
JVM的内存管理方式
java虚拟机将内存分为两大块,分别是线程私有区和共享数据区
![](https://img.haomeiwen.com/i11572843/c9d1e85581ca6e29.png)
线程私有区
线程私有区分为3个部分,分别是程序计数器,虚拟机栈和本地方法栈.
- 程序计数器用来指示执行下一行代码的地址,每个线程都有自己的程序计数器,程序计数器没有oom.
- 虚拟机栈即我们平时所说的栈,用来存放局部变量,方法返回地址等等,java虚拟机规范中定义了outofmemory和stackoverflow异常.
- 本地方法栈是jni c,c++代码的存放区域.java虚拟机规范中定义了outofmemory和stackoverflow异常.hotspotVM把虚拟机栈和本地方法栈合并为一个栈区.
共享数据区
共享数据区又分为两大块区域,他们分别是方法区和堆区.
- 方法区存放字面量和符号引用,即类信息,常量,静态变量,即时编译器编译后的代码,特殊的class对象.有oom异常.
方法区也叫运行时常量池
字面量: public static final ,java常量
符号引用: 方法名,类,接口全名 -
堆区是jvm最大的一块内存区域,是GC的主战场.也是我们进行内存优化主要的区.有oom异常.存放对象实例,数组内容.
image.png
GC垃圾回收器
java语言最大的两个特点,一个在于write a time,run any where.即跨平台性.一个在于垃圾回收机制,不用程序员自己去分配和回收内存.而是又jvm定时GC.这也造成了一些不规范代码引起的内存泄漏.我们需要去了解GC的回收机制,才能更好的解决内存优化问题.
那么GC是如何进行垃圾回收的呢?
GC如何确定一块内存是否应该回收
-
引用计数算法
原理:
引用计数算法很好理解,即如果有一个引用指向这块内存,这块内存的引用计数就+1,当GC扫描时,如果一块内存的引用计数为0,说明这块内存没有被使用,认为可以回收.
应用:
ios6中的手动计数mrc和自动计数arc
cocos2dx
缺点:
当两个对象相互引用时,计数永远不会为0.即使这两个对象不再被使用,也不会被回收. -
可达性分析算法
可达性分析.png
原理:
如上图,JVM会将生命周期比较长的对象作为GC Root,比如虚拟机栈正在运行使用的对象,静态属性,常量,JNI引用的对象. 判断一个对象是否能被回收,就看这个对象到GC Root是否可达
GC是需要两次扫描才会回收对象,第一次扫描标记是否可回收,第二次扫描才会回收.所以我们可以用finalize救活丢失引用的对象.
例如:
static App a;
@Overrride
protected void() throws Throwable{
super.finalize();
a = this;
}
- 回收和引用类型的关系
强引用:Object o = new Object();
软引用:SoftReference,当内存不足时回收,用于存放重要性不是很强单又不能随便被清除的对象,比如图片,当切换到后台时,内存不足可以回收这些图片,切换到前台时再次创建.
弱引用:WeackReference,GC第一次扫描时标记,第二次扫描时直接回收.
虚引用:PhantomReference,幽灵 幻影引用,不对内存造成影响,用于跟踪GC的回收通知
内存泄漏
当由于代码的不规范,造成一个对象没有被使,但是由于引用问题没有被回收,就会造成内存泄漏,当虚拟机没办法开辟新的内存时,就会引起oom异常.
我们可以用一些工具来查看程序运行时的内存占用情况,进而发现问题,解决问题.
-
Android Profile 的使用,
image.png
使用这个图标运行程序,会出现程序运行中的内存分配情况
image.png
-
Mat工具
下载: https://www.eclipse.org/mat/downloads.php
转换命令 hprof-conv -z src dst
select * from instanceof android.app.Activity
image.png
造成内存泄漏的代码可能是由于第三方或者安卓源码引起的,这是需要用反射去关闭第三方或者源码中多余的引用.
也有可能是自己代码引起的,这就需要在平时写代码中多多注意.
网友评论