Android内存优化 android内存性能优化

java虚拟机Java内存模型虚拟机栈( 线程私有 ):局部变量表、操作数栈、动态链接、方法出口等信息堆( 线程共享 ):实例对象方法区( 线程共享 ):类信息,常量,即时编译器编译后的代码程序计

本文最后更新时间:  2023-03-20 14:32:02

java虚拟机Java内存模型

虚拟机栈( 线程私有 ):局部变量表、操作数栈、动态链接、方法出口等信息堆( 线程共享 ):实例对象方法区( 线程共享 ):类信息,常量,即时编译器编译后的代码程序计数器( 线程私有 ):字节码行号指示器,记录当前线程执行到多少行本地方法栈( 线程私有 ):和虚拟机栈类似,两者的区别就是虚拟机栈是为虚拟机执行java方法服务,本地方法栈为虚拟机执行native方法服务 。

程序计数器

如果java方法正在线程中执行,计数器记录当前指令的地址,

如果是本机方法,计数器记录为空

大量

堆内存=新一代(1)+老一代(2)

新生代:复制算法老年代:标记整理算法

方法区域

也叫“永久生成”,1.8以后,方法区被移除,移到直接内存。

内存回收主要考虑堆区和方法区的回收,其他部分会以线程的生成和死亡为基础。

版本差异

1.6:运行常量池在方法区1.7:运行常量池在堆中1.8:删除方法区,引入直接内存,元空间概念,方法区中的静态变量被转移到堆中,只有class元数据在元空间。

堆中的老龄和方法区(永久代)是绑定的,不管哪边满了,都会触发两边的GC回收。

问题:

堆和栈的区别:栈:基本数据类型变量(int、short、long、byte、float、double、boolean、char)以及对象的引用变量堆:存储java对象堆中的对象对所有线程可见,栈内存只属于一个线程堆的内存空间远远大于栈为什么删除方法区?启动大小固定,很难调优,容易发生OOM元空间在本地内存中分配,本地内存足够就不会溢出

垃圾收集确定对象是否是活的。

引用计数算法(缺点:循环引用,技数永远不为0)可达性算法(二叉树中向下搜索,不存在引用链则对象不可用)

恢复算法

标记清除算法:标记完后对对象进行回收,使用在 老年代缺点:效率不高,标记和清除效率不高差生大量碎片空间,导致空间浪费复制算法:将可用对象复制到新的连续空间,删除之前的空间缺点:浪费50%的内存,复制长生存期的对象效率低下,所以该算法使用在 新生代标记整理算法:前期使用标记清除算法,后续使用整理算法,使对象排列称联系空间,使用在 老年代分代收集算法:对数据进行分代,每一代执行不同的回收算法

年轻一代分伊甸、s0、s1,分别是8: 1: 1,年轻一代和老一代是1: 2。

元空间的gc:元空间中的类加载器存活,则元空间中元数据也存活

小GC:清理年轻一代大GC:清理老一代全GC:清理整个堆空,包括年轻一代和永久一代。

四大引言简介简历

强引用:Strong Reference,通常使用的对象方式,gc不会回收软引用:SoftReference,当内存不足时进行回收弱引用:WeakReference,下一次gc时回收虚引用:PhantomReference,任何时候可回收

在处理内存泄漏问题时,弱引用是应用最广泛的,许多源代码和框架都使用它。

例如:

ThreadLocalMap中存储以ThreadLocal的弱引用为键,具体内容为valueGlide中缓存使用activeResource,存储的是图片的弱引用解决Handler的内存泄漏使用弱引用

理解引用

的所有引用都继承自Reference。以下是WeakReference的一个例子:

public class WeakReference<T> extends Reference<T> { /** * Creates a new weak reference that refers to the given object. The new * reference is not registered with any queue. * * @param referent object the new weak reference will refer to */ public WeakReference(T referent) { super(referent); } /** * Creates a new weak reference that refers to the given object and is * registered with the given queue. * * @param referent object the new weak reference will refer to * @param q the queue with which the reference is to be registered, * or <tt>null</tt> if registration is not required */ public WeakReference(T referent, ReferenceQueue<? super T> q) { super(referent, q); }}

有两种构造方法,区别在于是否传入引用队列。如果不是,则只有一种引用,不需要对引用队列进行链式存储。

public abstract class Reference<T> { private static boolean disableIntrinsic = false; private static boolean slowPathEnabled = false; //引用的对象,由垃圾回收器控制其引用 volatile T referent; /* Treated specially by GC */ final ReferenceQueue<? super T> queue; Reference queueNext; Reference<?> pendingNext; public T get() { return getReferent(); } @FastNative private final native T getReferent(); public void clear() { clearReferent(); } @FastNative native void clearReferent(); public boolean isEnqueued() { // Contrary to what the documentation says, this method returns false // after this reference object has been removed from its queue // (b/26647823). ReferenceQueue.isEnqueued preserves this historically // incorrect behavior. return queue != null && queue.isEnqueued(this); } public boolean enqueue() { return queue != null && queue.enqueue(this); } /* -- Constructors -- */ Reference(T referent) { this(referent, null); } Reference(T referent, ReferenceQueue<? super T> queue) { this.referent = referent; this.queue = queue; }}

抽象类很短,可以看出一个重点。Reference是一个节点,它保存next的引用。方法调用都使用ReferenceQueue方法,并直接转到:

private Reference<? extends T> head = null; private Reference<? extends T> tail = null; boolean enqueue(Reference<? extends T> reference) { synchronized (lock) { if (enqueueLocked(reference)) { lock.notifyAll(); return true; } return false; } } private boolean enqueueLocked(Reference<? extends T> r) { ... if (r instanceof Cleaner) { Cleaner cl = (sun.misc.Cleaner) r; cl.clean(); r.queueNext = sQueueNextUnenqueued; return true; } if (tail == null) { head = r; } else { tail.queueNext = r; } tail = r; tail.queueNext = r; return true; }

在加入团队的方法中,

使用synchronized添加锁,入队结束后释放锁,在ReferenceQueue中并不是标准的队列,使用的是Reference节点成链,行成单链表,类似于MessageQueue.如果是Cleaner类,创建一个虚引用节点,即不如队。Cleaner是用来释放非堆内存,所以做特殊处理

软引用

>public class SoftReference<T> extends Reference<T> { //时间戳,由gc更新 static private long clock; private long timestamp; public SoftReference(T referent) { super(referent); this.timestamp = clock; } /** * Creates a new soft reference that refers to the given object and is * registered with the given queue. * * @param referent object the new soft reference will refer to * @param q the queue with which the reference is to be registered, * or <tt>null</tt> if registration is not required * */ public SoftReference(T referent, ReferenceQueue<? super T> q) { super(referent, q); this.timestamp = clock; } public T get() { T o = super.get(); if (o != null && this.timestamp != clock) this.timestamp = clock; return o; }}

Gc管理时间戳

clock:上一次gc时间timestamp:访问get时最近一次的gc时间

回收条件是:时钟-时间戳

free_heep为堆空间空闲大小ms_per_mb是保留软引用时间/MB

幻象参考

public class PhantomReference<T> extends Reference<T> { public T get() { return null; } public PhantomReference(T referent, ReferenceQueue<? super T> q) { super(referent, q); }}

虚拟get方法在没有gc保留的情况下返回null。

虚拟引用可以通过将方法构造为持有对象引用来查看。

摘要:所有引用都是从引用基类继承的,引用基类是一个链表节点。ReferenceQueue通过这个点形成一个单独的链表,称为引用管理的队列。所有的引用都可以通过引用的isEnqueue方法来判断一个引用是否存在。

终结引用理解

在java堆中创建对象时,如果java类定义了finalize方法,将创建一个新的FinalizerReference类来指向新创建的对象。

记忆问题

内存泄漏:内存没有按照预期在gc时回收内存溢出:内存大小超出指定大小,导致OOM内存抖动:短时间创建大量内存对象,然后回收,导致内存发生锯齿形抖动,内存空间不连续加上碎片会导致更大的空间,最终OOM

内存优化意义

减少OOM,提高系统稳定性减少卡顿,提高流畅度减少内存占用,提高应用存活率减少异常发生和代码逻辑隐患

Android内存泄漏常见的内存泄漏

匿名内部类持有外部类引用,导致外部类内存泄漏(Handler)单例传入Context导致调用单例方无法被回收。非静态内部类创建静态实例注册与反注册资源对象关闭集和及时清理

内存泄漏检测

Profiler,Memory Analyzer(MAT)

Android studio有自己的内存、cpu、网络变化,可以详细分析。

LeakCanary

集成、自动检测内存泄漏、生成app以及提供内存泄漏堆栈。

原理:绑定生命周期。对于活动和片段,对象在打开时被放入弱引用队列中进行存储。gc被触发后,如果仍然存在,就会发生内存泄漏。

StrictMode(很少用)

一个较老的工具ThreadPolicy可以检测主线程是否访问网络和读写。VMPolicy检测内存、活动和碎片是否泄漏,以及资源是否正确关闭。

优化内存空

不必要的自动装箱

自动装箱是将基本数据类型转换成相应的复杂类型,HashMap的添加、删除、修改都存在很多自动装箱问题,所以尽量避免这些问题,比如用SparseArray、ArrayMap代替HashMap。

内存复用资源复用:通用字符串,颜色,布局视图复用:类似于RecyclerView的优化复用对象池:创建对象池,不用重复创建对象,类似于线程池,messae享元模式Bitmap对象复用:使用inBitmap属性可以告知Bitmap解码器尝试使用已经存在的内存区域,新解码的bitmap会尝试使用之前那张bitmap在heap中占据的pixel data内存区域。在App可用内存过低时主动释放内存在App退到后台内存紧张即将被Kill掉时选择重写Application中 onTrimMemory/onLowMemory 方法去释放掉图片缓存、静态缓存来自保。其他场景优化item被回收不可见时释放掉对图片的引用ListView :因此每次item被回收后再次利用都会重新绑定数据,只需在ImageView onDetachFromWindow的时候释放掉图片引用即可。RecyclerView :因为被回收不可见时第一选择是放进mCacheView中,这里item被复用并不会只需bindViewHolder来重新绑定数据,只有被回收进mRecyclePool中后拿出来复用才会重新绑定数据,因此重写Recycler.Adapter中的onViewRecycled()方法来使item被回收进RecyclePool的时候去释放图片引用。如果使用字符串拼接,尽量使用StringBuilder、StringBuffer(内存抖动)自定义view减少onDraw的耗时和执行次数尽量使用静态内部类尽量使用基础数据类型合适的时候使用软/弱引用

在线监测方案

常规监测当内存使用超过80%,使用 Debug.dumpHprofData(String fileName)获取dump文件回传至服务器,而后手动分析LeakCanary集成并带到线上Probe线上监测工具LeakInspectorResourceCanary

温馨提示:内容均由网友自行发布提供,仅用于学习交流,如有版权问题,请联系我们。