Android 面试必备 - JVM 及 类加载机制(1),2024年最新整理了3家面试问题:美团+字节+腾讯

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

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

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

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

如果你需要这些资料,可以添加V获取:vip204888 (备注Android)
img

正文

Java Heap是Java虚拟机所管理的内存中最大的一块,它是所有线程共享的一块内存区域。几乎所有的对象实例和数组都在这类分配内存。Java Heap是垃圾收集器管理的主要区域,因此很多时候也被称为“GC堆”。

根据Java虚拟机规范的规定,Java堆可以处在物理上不连续的内存空间中,只要逻辑上是连续的即可。如果在堆中没有内存可分配时,并且堆也无法扩展时,将会抛出OutOfMemoryError异常。

方法区(Method Area)

方法区也是各个线程共享的内存区域,它用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。方法区域又被称为“永久代”,但这仅仅对于Sun HotSpot来讲,JRockit和IBM J9虚拟机中并不存在永久代的概念。Java虚拟机规范把方法区描述为Java堆的一个逻辑部分,而且它和Java Heap一样不需要连续的内存,可以选择固定大小或可扩展,另外,虚拟机规范允许该区域可以选择不实现垃圾回收。相对而言,垃圾收集行为在这个区域比较少出现。该区域的内存回收目标主要针是对废弃常量的和无用类的回收。运行时常量池是方法区的一部分,Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Class文件常量池),用于存放编译器生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。运行时常量池相对于Class文件常量池的另一个重要特征是具备动态性,Java语言并不要求常量一定只能在编译期产生,也就是并非预置入Class文件中的常量池的内容才能进入方法区的运行时常量池,运行期间也可能将新的常量放入池中,这种特性被开发人员利用比较多的是String类的intern()方法。


JVM 垃圾回收器


垃圾回收器的类型大概有以下几种:

串行回收(Serial)和并行回收(Parallel):串行回收就是不过有多少个CPU,始终只用一个CPU来执行垃圾回收操作,而并行回收就是把整个回收工作拆分成多个部分,每个部分由一个CPU来负责,从而让多个CPU并行回收,并行回收的执行效率很高,但是复杂度增加,另外一个使内存碎片会增加。

并发执行和应用程序停止:应用程序停止的垃圾回收方式会在执行垃圾回收的同时导致应用程序的暂停,并发执行的垃圾回收虽然不会导致应用程序的暂停,但是由于并发执行垃圾回收需要解决和应用程序的执行冲突(应用程序可能会在垃圾回收过程中修改对象),因此,可能执行开销比应用程序停止更高,而且执行时也需要更多的内存。

压缩和不压缩:为了减少内存碎片,支持压缩的垃圾回收器会把所有的获得对象搬迁到一起,然后边界之前的占用内存全部回收。优点是:解决了内存碎片问题。缺点是速度较慢。

不压缩的垃圾回收方式:回收速度快,但分配内存慢,且无法解决内存碎片问题。

新生代和老年代

垃圾回收算法大概可分为以下几种算法

复制式的垃圾回收:把可达的对象复制到另外一个区域,再对这个区域另外清楚,回收过程不会长生内存碎片,但是需要复制数据和额外的内存。

标记-清除:先遍历一次,可达的标为可达,然后再遍历一次,把没标记为可达的进行回收。

标记-整理:从根开始访问,标可达,再遍历,把可达的复制到一起,回收不可达的内存

分代回收:按对象的存活时间,我们知道java的 大多数对象存活时间比较短,分为三个代,按代回收。

现代垃圾回收器设计思想

现行的垃圾回收器采用分代的方式来采用不用的回收设计,分代的基本思路是根据对象生存时间的长短,把堆内存分成3个代:

Young(年轻代):由1个Eden区和2个Survior构成,绝大多数对象先分配到Eden区中,当垃圾回收机制来临的时候,会把Eden去还存活的对象复制到survior区(其中一个),当surivior区满的时候,会把surivior还存活的对象存放到另外一个survior去中,当这个survior去也满的时候,会把从第一个survior区复制过来还存活的对象存放到老年代

Old(年老代):在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中,因此可以说存放生命周期比较长的对象

Permanent(永久代): 用于存放静态文件,如今Java类、方法等。持久代对垃圾回收没有显著影响持久代大小通过-XX:MaxPermSize=进行设置。

GC 的两种类型

GC有两种类型:Scavenge GC和Full GC。

Scavenge GC:一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区进行,不会影响到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden去能尽快空闲出来。一般使用复制算法。

Full GC:对整个堆进行整理,包括Young、Tenured和Perm。Full GC因为需要对整个对进行回收,所以比Scavenge GC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分工作就是对于FullGC的调节。有如下原因可能导致Full GC:

  • 年老代(Tenured)被写满

  • 持久代(Perm)被写满

  • System.gc()被显示调用

  • 上一次GC之后Heap的各域分配策略动态变化

对象如何实现自我救赎

我们知道要宣告一个对象死亡,至少需要两次标记。

第一次标记会从GC Root对象开始遍历,看是否存在相应的引用链,如果没有的话,那么 它 会被第一次标志并且进行第一次筛选,判断是否有必要执行筛选的条件是

是否覆盖了 Finalize()方法 ,覆盖了,有必要执行,没覆盖,没必要执行

finilize方法 是否被虚拟机调用 过,没有的话,有覆盖finalize方法,执行筛选

如果这个对象 被判定有必要执行 finalize方法 ,会把它放到F-Queue的队列当中 ,随后jvm会启动一个低优先级的线程来执行,如果在执行finalize 方法的 时候该对象有引用指向它,那么它可以实现自我救赎。


类加载过程


类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括加载、验证、准备、解析、初始化、使用、卸载。

其中类加载的过程包括了加载、验证、准备、解析、初始化五个阶段。在这五个阶段中,加载、验证、准备和初始化这四个阶段发生的顺序是确定的,而解析阶段则不一定,它在某些情况下可以在初始化阶段之后开始,这是为了支持Java语言的运行时绑定(也成为动态绑定或晚期绑定)。另外注意这里的几个阶段是按顺序开始,而不是按顺序进行或完成,因为这些阶段通常都是互相交叉地混合进行的,通常在一个阶段执行的过程中调用或激活另一个阶段。

“加载”(Loading)阶段是“类加载”(Class Loading)过程的第一个阶段,在此阶段,虚拟机需要完成以下三件事情:

  1. 通过一个类的全限定名来获取定义此类的二进制字节流。

  2. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。

  3. 在Java堆中生成一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口。

验证是连接阶段的第一步,这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。

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

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。

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


双亲委托机制


双亲委派模型:Bootstrap ClassLoader、Extension ClassLoader、ApplicationClassLoader。

  1. 启动类加载器,负责将存放在<JAVA_HOME>\lib目录中的,或者被-Xbootclasspath参数所指定的路径中,并且是虚拟机识别的(仅按照文件名识别,如rt.jar,名字不符合的类库即时放在lib目录中也不会被加载)类库加载到虚拟机内存中。启动类加载器无法被java程序直接引用。

  2. 扩展类加载器:负责加载<JAVA_HOME>\lib\ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用该类加载器。

  3. 应用程序类加载器:负责加载用户路径上所指定的类库,开发者可以直接使用这个类加载器,也是默认的类加载器。

三种加载器的关系:启动类加载器->扩展类加载器->应用程序类加载器->自定义类加载器。

推荐学习资料

  • Android进阶学习全套手册

  • Android对标阿里P7学习视频

  • BAT TMD大厂Android高频面试题

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
img

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

果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-vrnmAyfj-1713440570154)]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值