从Java垃圾回收机制窥探内存优化
作为Android工程师,我看过很多关于Android内存泄漏的相关优化的文章,其中大部分都是告诉你该怎么做,做哪些,列一些具体的措施。而从来也没有解释为什么要这么做。
今天,我想从这方面内存优化的背后的机理入手,去浅浅的探究一下背后的准则。但是,要想研究Java的内存优化,就必须研究Java辣鸡回收机制。而想要能基本理解GC,就必须了解Java程序运行时的内存区域。没办法,学习学全套嘛。
况且,我是专业的。
下面,就是Java程序运行时的内存模型
内存模型.png- 当你的Java字节码执行起来的时候,虚拟机就会它所管理的内存大致分成这五个部分,把你的代码分别扔到这五个框框里:
- 方法区:用于存储类信息,常量静态变量等等这些
- 本地方法栈:用于为本地方法的执行提供服务,pass
- 栈:严格来说它叫虚拟机栈,是虚拟机执行Java方法的重要内存模型。同时存储局部变量,对象引用(有了这个,就能找到对象)等等。
- 堆:存储对象实例的区域(比如 A a=new A(),那么 a这个引用就保存在栈中,而new A() 的这个对象就放在堆中)
- 程序计数器:用来记录当前线程执行的字节码的地址,指示执行引擎执行代码。
执行引擎:哎呀卧槽,刚才执行到哪一步来着?
程序计数器:辣鸡。
讲到这里,你可能开始有点晕了,我的错,这是年轻的时候写作文经常爱凑字数落下的老毛病,以至于一动笔就开始拉家常。你们把刀放下来,听我继续讲。
你写的程序数据被分配到那五个区域之后。躲在暗处的那个饥渴难耐端的垃圾回收机制就马上跳出来了。
但是也不是每个区域他都去回收垃圾,不要以为只有人是势利的,其实GC程序也是很势利很现实的,它从来都只去那些容易收到垃圾的地方去收垃圾。也就是堆中。因为堆占据了Java运行时内存的大头........当然了,我是开玩笑的,实际上GC去哪里回收垃圾是规定好的。
- “组织已经决定了,以后你就在堆里面回收垃圾了”(垃圾堆的历史来由我们就讲到这里。。。)
嗯,终于要到内存优化的地方了。前面已经讲到,Java会专注的在堆中回收垃圾,而堆中基本上之存放对象实例,所以内存回收的重点就是对象实例!!
那么,GC回收对象的准则什么呢?答案是GC Roots引用链
- 通过这个引用链往下搜索,所有在这些链条上的,都是“活着的”对象,其余的就被回收了。
其实我知道,就算我解释那么多你也不太懂,没关系,我来画一画就好了,做事做全套嘛,
况且,我是专业的。
引用链.png- GC程序从栈顶开始一直找到栈底,寻找每个对象引用所对应的对象实例,然后在实例中寻找是否有指向其他对象的引用,若有,继续找(这一部分并没有画出,但是也是重点),没有,则回到栈中,继续往栈底搜索。
- 当然,GC不止会在栈中寻找引用,还会在静态存储区(存储静态变量的地方,猜猜看对应着上述那个区域)中寻找引用,(因为有的堆中的对象在栈中没有引用,它的引用在静态存储区,类似于 static A a =new A() )
好了,我们终于把Java内存回收的机理讲完了,那么内存优化背后的机理也基本被我们扒开了:
- 不再使用对象时,将引用置空,那么GC顺着栈或者静态存储区中的引用就找不到堆中对应的对象,那么这个对应的对象就会被回收。
- 尽量不要在生命周期较长(程序执行期间可能都不会被收回)的对象实例中引用其他的不必要的占内存较大的对象实例
那么对应Android的内存优化的建议就很直观了,基本围绕了一个点:
- Android中Activity等占据较大的内存空间对象,在不用的时候,一定保证当前对象的直接引用和间接引用全部被置为空。内存才能被释放。否则,就会有内存泄漏。
Java内存优化最根本的准则,就是努力使你的程序适配Java的GC机制。
你很可能有点不耐烦,觉得其实我可以几句话就把事情说清楚,却扯一大堆幺蛾子,浪费你宝贵时间。
我说过了,凑字数是我的老毛病了,况且,我是还是专业的。