字节-Android开发面经(七)
共 3469字,需浏览 7分钟
· 2021-06-07
点击蓝字关注我们,获取更多面经
一、定义
内存溢出: 即为out of memory, 当你要求分配的内存超过了系统给你的内存时, 系统就会抛出out of memory的异常(每个Android能用的内存是有限的)
比如: 当前应用只剩下4M的空间可用, 但你却加载得到一个需要占用5M空间的图片Bitmap对象, 就会抛出溢出的异常
内存泄露: 即为memory leak, 一个对象被创建后, 你不再使用它了, 但因为某种原因它又没有成为垃圾对象, 这块内存不能再被分配置使用.
比如: 查询数据库得到的cursor对象在使用完后没有关闭, Activity中使用Handler发延迟消息, 但退出前不移除未处理的消息
内存泄露不多时没有太大影响, 但积累得多了就会导致应用运动缓慢, 到最后就会内存溢出.
二、内存泄漏的分类
常发性内存泄漏: 发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏
偶发性内存泄漏: 发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的
一次性内存泄漏: 发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏
说明: 危害性大小顺序为: 1)>2)>3)
三、造成内存泄露的几种场景
长生命周期的对象持有短生命周期对象的引用: Activity中使用Handler
资源数据连接相关对象不关闭: cusor, stream, connection
HashSet中的对象或HashMap中的Key对象, 内部与hash值相关的属性被修改
一些对象产生后不会自动释放或需要完全执行完了才释放. 比如: Bitmap, Thread, AsyncTask
四、避免内存泄露
尽早释放无用对象的引用
使用字符串处理,避免使用String,应大量使用StringBuffer,每一个String对象都得独立占用内存一块区域
尽量少用静态变量,因为静态变量存放在永久代(方法区),永久代基本不参与垃圾回收
避免在循环中创建对象
五、造成内存溢出的的场景
申请了太多的对象. 比如: 使用ListView时, 不复用convertView, 当数据项多时就会出现内存溢出
创建的对象内存太大. 比如: 不经过压缩直接加载大图片文件
内存泄露积累一定的时间后就可能出现
六、避免内存溢出
通过复用对象的方式, 减少产生的对象
大对象需要先压缩后创建
避免或减少内存泄露的情况
sleep
让当前线程休眠指定时间。
休眠时间的准确性依赖于系统时钟和CPU调度机制。
不释放已获取的锁资源,如果sleep方法在同步上下文中调用,那么其他线程是无法进入到当前同步块或者同步方法中的。
可通过调用interrupt()方法来唤醒休眠线程。
wait
让当前线程进入等待状态,当别的其他线程调用notify()或者notifyAll()方法时,当前线程进入就绪状态
wait方法必须在同步上下文中调用,例如:同步方法块或者同步方法中,这也就意味着如果你想要调用wait方法,前提是必须获取对象上的锁资源
当wait方法调用时,当前线程将会释放已获取的对象锁资源,并进入等待队列,其他线程就可以尝试获取对象上的锁资源。
定义在一个类内部的类叫内部类,包含内部类的类称为外部类。内部类可以声明public、protected、private等访问限制,可以声明 为abstract的供其他内部类或外部类继承与扩展,或者声明为static、final的,也可以实现特定的接口。外部类按常规的类访问方式使用内部 类,唯一的差别是外部类可以访问内部类的所有方法与属性,包括私有方法与属性。
(1)创建实例
OutClass.InnerClass obj = outClassInstance.new InnerClass(); //注意是外部类实例.new,内部类
AAA.StaticInner in = new AAA.StaticInner();//注意是外部类本身,静态内部类
(2)内部类中的this
内 部类中的this与其他类一样是指的本身。创建内部类对象时,它会与创造它的外围对象有了某种联系,于是能访问外围类的所有成员,不需任何特殊条件,可理 解为内部类链接到外部类。用外部类创建内部类对象时,此内部类对象会秘密的捕获一个指向外部类的引用,于是,可以通过这个引用来访问外围类的成员。
(3)外部类访问内部类
内部类类似外部类的属性,因此访问内部类对象时总是需要一个创建好的外部类对象。内部类对象通过‘外部类名.this.xxx’的形式访问外部类的属性与方法。如:
System.out.println("Print in inner Outer.index=" + pouter.this.index);
System.out.println("Print in inner Inner.index=" + this.index);
(4)内部类向上转型
内部类也可以和普通类一样拥有向上转型的特性。将内部类向上转型为基类型,尤其是接口时,内部类就有了用武之地。如果内部类是private的,只可以被它的外部类问,从而完全隐藏实现的细节。
(5)方法内的类
方法内创建的类(注意方法中也能定义类),不能加访问修饰符。另外,方法内部的类也不是在调用方法时才会创建的,它们一样也被事先编译了。
(6)静态内部类
定义静态内部类:在定义内部类的时候,可以在其前面加上一个权限修饰符static。此时这个内部类就变为了静态内部类。
通常称为嵌套类,当内部类是static时,意味着:
[1]要创建嵌套类的对象,并不需要其外围类的对象;
[2]不能从嵌套类的对象中访问非静态的外围类对象(不能够从静态内部类的对象中访问外部类的非静态成员);
嵌套类与普通的内部类还有一个区别:普通内部类的字段与方法,只能放在类的外部层次上,所以普通的内部类不能有static数据和static字段, 也不能包含嵌套类。但是在嵌套类里可以包含所有这些东西。也就是说,在非静态内部类中不可以声明静态成员,只有将某个内部类修饰为静态类,然后才能够在这 个类中定义静态的成员变量与成员方法。
另外,在创建静态内部类时不需要将静态内部类的实例绑定在外部类的实例上。普通非静态内部类的 对象是依附在外部类对象之中的,要在一个外部类中定义一个静态的内部类,不需要利用关键字new来创建内部类的实例。静态类和方法只属于类本身,并不属于 该类的对象,更不属于其他外部类的对象。
(7)内部类标识符
每个类会产生一个.class文件,文件名即为类名。同样,内部类也会产生这么一个.class文件,但是它的名称却不是内部类的类名,而是有着严格的限制:外围类的名字,加上$,再加上内部类名字。
(8)为何要用内部类?
1. 内部类一般只为其外部类使用;
2. 内部类提供了某种进入外部类的窗户;
3. 也是最吸引人的原因,每个内部类都能独立地继承一个接口,而无论外部类是否已经继承了某个接口。因此,内部类使多重继承的解决方案变得更加完整。
更多面经
扫描二维码
获取更多面经
扶摇就业