1、java虚拟机
运行时数据区域:
分析:
常量池:存放字面量和符号引用,如:字面量public satic final java常量,符号引用 类,接口全名,方法名
方法区:类信息、常量、静态变量,即时编译器编译后的代码,特殊的Class对象,ClassLoader加载类信息
堆:存放对象实例、数组;虚拟机能管理的最大的一块内存 GC的主战场,会发生OOM
程序计数器PC:相当于一个执行代码的指示器,用来确认下一行执行的代码的地址;每个线程都有一个;没有OOM的区
虚拟机栈:我们平时说的栈就是这块区域,java虚拟机规范中定义了outofmemory和stackoverflow异常,没有内存碎片
本地方法栈:java虚拟机规范中定义了outofmemory和stackoverflow异常,是用于和C,C++打交道的地方,比如:JNI,NDK等都会用到。
共享数据区是所有线程都能访问的区域,
2、GC是如何确定内存回收?
引用计数算法:
Object o1 = new Object(); 计数 + 1 = 1
Object o2;
o2 = o1; 计数 + 1 = 2
o1 = null; 计数 - 1 = 1 计数为1 o1和o2都不会回收
Android中使用的是可达性分析算法
1):虚引用的情况
虚引用在处理对象时,只对对象进行记录,不对对象的生命周期进行处理,GC回收之后,可以得到一个通知
@Test
public void testPhantomReference() throws InterruptedException {
//虚引用:功能,不会影响到对象的生命周期的
//但是能让程序员知道该对象什么时候被回收了
//引用队列
ReferenceQueue<Object> objectReferenceQueue = new ReferenceQueue<>();
//对象
Object phantonObject = new Object();
//虚引用
PhantomReference phantomReference = new PhantomReference(phantonObject,objectReferenceQueue);
//置空,将引用断开(对应为null,引用队列也为null)
phantonObject = null;
System.out.println("phantonObject: " + phantonObject); // 为 null了
System.out.println("phantomReference: " + phantomReference.get());//因为不对对象处理,这里就为 null
System.out.println("objectReferenceQueue: " + objectReferenceQueue.poll()); //null
//GC开始扫内存了,在扫内存之前,先把上面断开的引用放到引用队列中(引用队列就有数据了)
System.gc();
Thread.sleep(2000);
System.out.println("objectReferenceQueue: " + objectReferenceQueue.poll());//打印:java.lang.ref.ReferenceQueue@2957fcb0
}
打印:
phantonObject: null
phantomReference: null
objectReferenceQueue: null
objectReferenceQueue: java.lang.ref.ReferenceQueue@2957fcb0
2)软引用:
对象被回收之前,对象是可以拿到用
@Test
public void testWeakReference() throws InterruptedException {
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
Object weakObject = new Object();
//软引用
WeakReference weakReference = new WeakReference(weakObject,referenceQueue);
//在对象被回收前,对象是可以被拿到用的,这里有数据,可以get到
System.out.println("WeakReference: " + weakReference.get());
System.out.println("referenceQueue: " + referenceQueue.poll());
//置空之后,引用断开,
weakObject = null;
//GC来处理内存,之后软引用就拿不到数据了
System.gc();
Thread.sleep(2000);
System.out.println("WeakReference: " + weakReference.get());
System.out.println("referenceQueue: " + referenceQueue.poll());
}
软引用:内存不足了才回收:后台信息
弱引用:GC来的时候会回收,处理内存泄露常用此引用。
虚引用:可以跟踪GC时使用
3、内存泄露问题:
产生的原因:一个长生命周期的对象持有一个短生命周期对象的引用通俗讲就是该回收的对象,因为引用问题没有被回收,最终会产生OOM。
打开AndroidStudio,运行你的项目,
一会运行出来就可以看到下面的情况:
在上图中下面的样式找到这个:
然后找到你的包,然后,可以通过每一个activity的使用情况
看到每个类的使用情况以及内存占用情况,
我们还可以使用第三方工具,MemoryAnalyzer 去查看,这个工具网上下载,
首先我们在androidstudio中上图的内容界面中先鼠标选中一块,然后右键选则第一个导出;
导出之后,还需要把导出的文件转换profile文件格式,
打开cmd,进入到你刚入的文件目录下,我这里在e盘temp文件夹下,执行一下命令:
先执行:hprof-conv
在执行:hprof-conv -z 1.hprof 1-mat.hprof
就可以了,
上面的在工具中导入时把文件名改成了1.hprof ,所以我在执行命令时,把1.hprof 转成 1-mat.hprof
好了,转换成功,下面打开MemoryAnalyzer ,
打开找到你转换之后的文件
在监听指向位置输入你要查询的那个activity问题:
我这里没有内存泄露,如果,你鼠标选中最后的那个右键
查看强引用的情况::
由于我这里没有内存泄露,所以啥也没有,如果你有内存泄露,下面就会看到有多少个有问题的情况:
这里找一个有问题的截图,箭头方向就是互相引用导致的问题,
比如这里,又看到被两个地方引用,我把可以看到,然后鼠标放上去,右键复制
然后把代码粘贴到你的项目中任何一个位置,比如activity中,然后点击这个类去查看代码中的使用情况:
点击它即可进去查看,找到:mServedView,mNextServedView;
然后确定它是否在哪里引用导致的,如果是在MainActivity中导致的,我们就可以在这个类中处理,当ManinActivity销毁时把这两个引用清空,具体就不多做介绍,找到问题点,其它的就交给你了。