Android面试(JVM部分,含存储区、GC、内存溢出泄漏优化)(1)

本文详细解析了Java内存管理中的垃圾回收机制,包括引用计数法、可达性分析算法,以及分代收集算法(如复制、标记-整理等)。同时介绍了内存分配规则,讨论了内存泄漏、内存溢出及其解决方案,以及类加载过程和双亲委派模型。
摘要由CSDN通过智能技术生成

================

(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中验证:

(1)文件格式验证:验证字节流是否符合Class文件的规范,如主次版本号是否在当前虚拟机范围内,常量池中的常量是否有不被支持的类型。

(2)元数据验证:对字节码描述的信息进行语义分析,如这个类是否有父类,是否继承了不能被继承的类等。

(3)字节码验证:这是验证过程最复杂的阶段,通过验证数据流和控制流的分析,确定程序语义是否正确,主要针对方法体的验证。如:方法中的类型转换是否正确,跳转指令是否正确等。

(4)符号引用验证:这个动作在后面的解析过程中发生,主要是为了确保解析工作能正确执行。

7.3准备:为类的静态变量分配内存并将其初始化为默认值,这些内存都在静态区进行分配。此阶段不分配类中的实例变量的内存,实例变量将会在对象实例化时随着对象一起分配在Java堆中。

public static int num = 2;//在准备阶段num为0,初始化阶段才会为2.

7.4解析:主要完成符号引用到直接引用的转换动作,解析动作并不一定在初始化完成之前,也有可能是之后。

7.5初始化:是类加载的最后一步,前面的类加载过程,除了在加载阶段用户应用程序可以通过自定义类加载器参与之外,其余阶段完全由虚拟机主导和控制。到了初始化阶段,在真正开始执行类中定义的Java程序代码。

7.2、类加载器双亲委派模型机制


当一个类收到了类加载请求时,不会自己先去加载这个类,而是将其委派给父类,由父类去加载,如果此时父类不能加载,反馈给子类,则由子类去完成类的加载。

7.3、类加载器的定义和种类


定义:实现通过类的权限定名获取该类的二进制字节流的代码块叫做类加载器。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

最后,面试前该准备哪些资源复习?

其实客户端开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。

这里再分享一下我面试期间的复习路线:(以下体系的复习资料是我从各路大佬收集整理好的)

《Android开发七大模块核心知识笔记》

面试字节两轮后被完虐,字节面试官给你的技术面试指南,请查收

面试字节两轮后被完虐,字节面试官给你的技术面试指南,请查收

《960全网最全Android开发笔记》

面试字节两轮后被完虐,字节面试官给你的技术面试指南,请查收

《379页Android开发面试宝典》

历时半年,我们整理了这份市面上最全面的安卓面试题解析大全
包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。

《507页Android开发相关源码解析》

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
img
轮技术面试的几率。

《507页Android开发相关源码解析》

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-hoopZJDw-1712936325766)]

  • 24
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值