(1)在RAM中,由编译器自动分配释放,存放函数的参数和局部变量的值,即存放基本数据类型(int、short、double、long、float、boolean、char、byte)和对象的引用。如方法体内声明参数int a,只要栈的剩余空间大于需要的内存空间,则系统自动在栈区为b分配空间。否则会报异常提示栈溢出。(2)栈区是向低地址扩展的,连续的内存区域,栈顶地址和栈的容量是设定好的,1M或2M或其他。(3)申请效率上,由系统分配,速度很快,仅次于寄存器。(4)存储顺序一般是先函数参数,再是局部变量。(5)生命周期是确认的,没有引用指向数据时,数据会消失。(6)数据共享,如int a=9;b=9;ab都指向c
堆区(heap)
(1)程序员手动分配释放,存放new出来的对象。操作系统有一个记录空闲内存地址的链表,当需要申请堆区空间时,会遍历该链表寻找第一个大于此空间的堆结点,然后将该结点从空闲地址链表删除,将该结点分配给程序,并在这块内存区间首地址记录内存大小,方便以后内存释放,并将剩下的空间重新放到空闲内存地址链表中。(2)堆是向高地址扩展的,不连续的内存区域,因为它是由链表存储的内存区域,所以是不连续的,链表的遍历就是由低地址向高地址的,堆的大小受限于计算机中有效的虚拟内存空间,所以堆获得的空间比较灵活,也比较大。(3)申请效率上,一般是new,速度较慢,容易产生内存碎片。(4)存储顺序显示内存块大小,其他由程序员决定。(5)内存分为新生代和老年代,永久代(5)由垃圾回收器进行回收。
全局区(静态区)
存放全局变量和静态变量,初始化的在初始化区,未初始化的在相邻的未初始化区,由系统释放。
常量池
存放常量,对于相等的字符串,常量池中只有一份,由系统释放
程序代码区
存放函数体的二进制代码非arm(随机访问存储器)存储如硬盘等永久存储空间
2、GC机制
================
(1)如何确定哪些对象可以回收?(可达性分析算法)
引用计数法:判断对象的引用数量。实现方法:给对象添加引用计数器,当有引用时就+1,引用完成并失效时就-1,当计数器的值为0时,则被视为失效的垃圾对象,则可以被回收。但是这个方法不能解决循环引用问题,如对象a,b互相引用,则gc机制无法识别,所以没有采用这个机制。
可达性分析算法:通过判断对象的引用链是否可达来决定对象是否可以被回收。实现方法:把所有的引用关系看作一张图,通过一系列的名为GCRoot的根节点作为起始点开始往下搜索,走过的路就叫引用链,当一个对象没有任何引用链连接GcRoot时,则认为不可达,则可以回收
(2)jvm会在什么时候回收?
CPU空闲时回收;堆内存满了时回收;主动调用System.gc()时回收。
当新生代内存不够用时为Minor GC,当JVM内存不够用时为Full GC。
(3)gc怎么回收?使用最多的是分代收集算法
标记-清除算法:最基础的算法,先标记,回收时将带有标记的内存都回收。虽然简单,但标记和清除的效率都很低,并且清除内存后会造成内存碎片化,当需要保存较大对象时无法找到足够的连续内存来保存,造成空间浪费。
复制算法:为了解决内存碎片化问题,将内存分为大小相等的两块AB,每次只使用一块内存,如这次使用A块,当垃圾回收时,就将A上还活跃的对象复制到B内存中,然后一次性清理掉A中的内存空间。这样不会有内存碎片化问题,但总是复制,效率变低,关键是内存还少了一半儿。所以将该算法进行了改进,内存区域不再划分为大小相等的两块,而是以8:1:1的比例分为Eden区,和两个Survivor区,每次都会优先使用Eden区,当Eden区满了,就将活跃对象复制到survivor区,若此时存活的对象太多导致survivor区不够时,就会通过分配担保机制将对象复制到老年代中。
标记-整理算法:和标记-清除算法类似,内存回收后,将存活的对象向左移动,整理到一起,避免产生碎片化。
分代收集算法:jvm使用最多的算法,根据对象的生命周期,将堆分为新生代和老年代。在新生代中,由于对象生存期短,每次回收都会有大量对象死去,所以采用复制算法。老年代里对象存活率高,所以采用标记-整理算法或者标记-清除算法。
3、内存分配
==========
3.1,jvm内存有3个区域:
新生代:一般都是新生成的对象,目标是尽可能快速的回收那些生命周期短的对象,对象从这个区域被回收的过程为Minor GC,因为每次回收都有大量对象被回收,少量存货,所以使用的是复制算法。空间分配为一个伊甸园空间Eden,两个幸存者空间SurvivorA,B,每次垃圾回收时,Eden里存活的对象复制到幸存者A中,然后清空Eden,当A满了,就将A中存活的复制到B,清空A,然后AB互换身份继续循环下去。当循环n次(一般是15次)后,依然存活的被转到老年代。
老年代:一般是生命周期较长的对象。空间比新生代大,GC次数比新生代少,老年代满了时会回收,回收时间长,回收过程为Major GC或Full GC,因为对象存活率高,所以使用的是标记-整理算法
永久代:存放静态文件,如Java类常量,字符串常量等,回收过程为Major GC,只有所有实例被回收,ClassLoader被回收,Class对象无法通过任何途径访问包括反射时,才会发生回收机制。
3.2,内存分配
== :对基本数据类型比较的是值,对引用数据类型比较的是地址。
equals:只能比较引用数据类型,基本数据类型没有此方法,如果没有重写Object的equals方法,则该方法返回的结果就是==的结果,重写后的equals方法比较的是对象中的属性,如String类就重写了equals方法,比较的是对象的值。
String s1 = new String("helloworld");//创建了两个对象,“”引起来的在常量池中,new出的对象在堆中。
c,c1,c2的地址值相同,都在常量池中,所以返回true。d相当于d=new String(a+b),是在堆内存中,地址不同所以返回false。
4、内存泄漏及解决方法
==================
指程序中动态分配内存给一些临时对象,对象可达无法被GC回收,但对象已无用。
4.1场景:
长生命周期的对象持有短生命周期对象的引用,如全局静态map中缓存局部变量,但没有做清空操作,map会越来越大,造成内存泄漏;
非静态内部类持有外部类的引用:如使用Handler时,当activity被销毁了,handler发送的message没有执行完毕,那么handler就不会被回收。但是由于非静态内部类默认持有外部类的引用,又因为handler可达,并持有activity引用,所以jvm会错误的认为activity可达而不执行GC,这时activity就造成内存泄漏。
4.2解决方法
尽在释放无用对象的引用;
5、内存溢出
==========
内存泄漏时内存溢出的主要原因,但不是唯一原因。
堆内存溢出:新生代中survivor和老年代仍然无法存放Eden复制过来的对象,导致无法在Eden中new对象。
方法区(全局区)内存溢出:加载的类过多,或者使用反射等生成的动态代理类过多时。
线程栈溢出:每个线程都独有一块内存结构,当递归太深或者调用层级过多时会导致溢出。
解决方法:加大JVM内存大小;减少String的使用,可以用StringBuffer,StringBuilder替代;尽量少用静态变量,因为静态变量放在全局区(方法区,永久代),基本不会GC;避免在循环中创建对象;不要一次性加载过多数据,如数据库取数据;内存泄漏的解决方法都加上;
6、WebView内存泄露
=================
(1)通过布局的viewgroup,使用viewgroup.add方式添加webview,销毁的时候先通过viewgroup.removeView(webview)删除webview,然后webview.destroy;另外传入webview的Context采用弱引用的方式
(2)新开进程,关闭页面将进程关闭
7、类加载
=====
7.1、7个过程
主要就是加载、验证、准备、解析、初始化、使用、卸载。简单说就是虚拟机把描述类的数据从Class文件加载到内存,并对数据进行验证、解析和初始化,最终形成可以被虚拟机直接使用的Java类型。
7.1加载:加载是类加载的第一个过程,在这个过程,将完成以下3件事:
(1)通过一个类的全限定名获取该类的二进制流。
(2)将该二进制流中的静态存储结构转化为方法去运行时数据结构。
(3)在内存中生成该类的Class对象,作为该类的数据访问入口。
7.2验证:目的是为了确保Class文件的字节流中的信息不会危害到虚拟机,在该阶段主要完成以下4中验证:
面试复习路线,梳理知识,提升储备
自己的知识准备得怎么样,这直接决定了你能否顺利通过一面和二面,所以在面试前来一个知识梳理,看需不需要提升自己的知识储备是很有必要的。
关于知识梳理,这里再分享一下我面试这段时间的复习路线:(以下体系的复习资料是我从各路大佬收集整理好的)
资料获取方式:前往我的GitHub
- 架构师筑基必备技能
- Android高级UI与FrameWork源码
- 360°全方面性能调优
- 解读开源框架设计思想
- NDK模块开发
- 微信小程序
- Hybrid 开发与Flutter
知识梳理完之后,就需要进行查漏补缺,所以针对这些知识点,我手头上也准备了不少的电子书和笔记,这些笔记将各个知识点进行了完美的总结:
《960全网最全Android开发笔记》
《379页Android开发面试宝典》
历时半年,我们整理了这份市面上最全面的安卓面试题解析大全 包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。
如何使用它?
1.可以通过目录索引直接翻看需要的知识点,查漏补缺。 2.五角星数表示面试问到的频率,代表重要推荐指数
《507页Android开发相关源码解析》
只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。
真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。