内存,是Android应用的生命线,一旦在内存上出现问题,轻者内存泄漏,重者直接crash,因此一个应用保持健壮,内存这块的工作是持久战,而且从写代码这块就需要注意合理性,所以想要了解内存优化如何去做,要先从基础知识开始。
1 JVM内存原理
这一部分确实很枯燥,但是对于我们理解内存模型非常重要,这一块也是面试的常客
image.png
从上图中,我将JVM的内存模块分成了左右两大部分,左边属于共享区域(方法区、堆区),所有的线程都能够访问,但也会带来同步问题,这里就不细说了;右边属于私有区域,每个线程都有自己独立的区域。
1.1 方法执行流程
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
execute()
}
private fun execute(){
val a = 2.5f
val b = 2.5f
val c = a + b
val method = Method()
val d = getD()
}
private fun getD(): Int {
return 0
}
}
class Method{
private var a:Int = 0
}
我们看到在MainActivity的onCreate方法中,执行了execute方法,因为当前是UI线程,每个线程都有一个Java虚拟机栈,从上图中可以看到,那么每执行一个方法,在Java虚拟机栈中都对应一个栈帧。
每次调用一个方法,都代表一个栈帧入栈,当onCreate方法执行完成之后,会执行execute方法,那么我们看下execute方法。
execute方法在Java虚拟机栈中代表一个栈帧,栈帧是由四部分组成:
(1)局部变量表:局部变量是声明在方法体内的,例如a,b,c,在方法执行完成之后,也会被回收; (2)操作数栈:在任意方法中,涉及到变量之间运算等操作都是在操作数栈中进行;例如execute方法中:
val a = 2.5f
当执行这句代码时,首先会将 2.5f压入操作数栈,然后给a赋值,依次类推
(3)返回地址:例如在execute调用了getD方法,那么这个方法在执行到到return的时候就结束了,当一个方法结束之后,就要返回到该方法的被调用处,那么该方法就携带一个返回地址,告诉JVM给谁赋值,然后通过操作数栈给d赋值
(4)动态链接:在execute方法中,实例化了Method类,在这里,首先会给Method中的一些静态变量或者方法进行内存分配,这个过程可以理解为动态链接。
1.2 从单例模式了解对象生命周期
单例模式,可能是众多设计模式中,我们使用最频繁的一个,但是单例真是就这么简单吗,使用不慎就会造成内存泄漏!
interface IObserver {
fun send(msg:String)
}
class Observable : IObserver {
private val observers: MutableList<IObserver> by lazy {
mutableListOf()
}
fun register(observer: IObserver) {
observers.add(observer)
}
fun unregister(observer: IObserver) {
observers.remove(observer)
}
override fun send(msg: String) {
observers.forEach {
it.send(msg)
}
}
companion object {
val instance: