『美團(tuán)網(wǎng)』2019最新社招Java面試題分享——Spring+JVM+多線程

一. Spring

1. 談?wù)勀銓?duì)Spring的理解

關(guān)鍵點(diǎn)

  • 企業(yè)框架,目前最流行,沒有之一
  • AOP、IOC、Spring MVC

2. Spring中用到了哪些設(shè)計(jì)模式

  • 工廠模式,比如 BeanFactory
  • 代理模式,在Aop實(shí)現(xiàn)中用到了JDK的動(dòng)態(tài)代理
  • 單例模式,Bean的創(chuàng)建默認(rèn)就是單利的

3. IoC的啟動(dòng)過程

  • Resource文件的定位,即找到bean的配置文件
  • 通過特定的reader解析該bean配置文件,抽象成beanDefinition類
  • 將beanDefinition向容器注冊(cè),寫入到一個(gè)大的HashMap中

4. BeanFactory 和 ApplicationContext 區(qū)別

  • 功能,BeanFactory負(fù)責(zé)讀取bean配置,管理bean的加載,實(shí)例化,維護(hù)bean之間的依賴關(guān)系,負(fù)責(zé)bean的聲明周期;ApplicationContext除了提供上述BeanFactory所能提供的功能之外,還提供了國際化支持、資源訪問、事件傳遞、隊(duì)Web的支持等功能
  • BeanFactroy采用的是延遲加載形式來注入Bean的,即只有在使用到某個(gè)Bean時(shí)(調(diào)用getBean()),才對(duì)該Bean進(jìn)行加載實(shí)例化;而ApplicationContext則相反,它是在容器啟動(dòng)時(shí),一次性創(chuàng)建了所有的Bean。
  • BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但兩者之間的區(qū)別是:BeanFactory需要手動(dòng)注冊(cè),而ApplicationContext則是自動(dòng)注冊(cè)

5. Bean 的生命周期

  • 實(shí)例化一個(gè)Bean,也就是我們通常說的new
  • 按照Spring上下文對(duì)實(shí)例化的Bean進(jìn)行配置,也就是IOC注入
  • 如果這個(gè)Bean實(shí)現(xiàn)了BeanNameAware接口,會(huì)調(diào)用它實(shí)現(xiàn)的setBeanName(String beanId)方法,此處傳遞的是Spring配置文件中Bean的ID
  • 如果這個(gè)Bean實(shí)現(xiàn)了BeanFactoryAware接口,會(huì)調(diào)用它實(shí)現(xiàn)的setBeanFactory(),傳遞的是Spring工廠本身(可以用這個(gè)方法獲取到其他Bean)
  • 如果這個(gè)Bean實(shí)現(xiàn)了ApplicationContextAware接口,會(huì)調(diào)用setApplicationContext(ApplicationContext)方法,傳入Spring上下文,該方式同樣可以實(shí)現(xiàn)步驟4,但比4更好,以為ApplicationContext是BeanFactory的子接口,有更多的實(shí)現(xiàn)方法
  • 如果這個(gè)Bean關(guān)聯(lián)了BeanPostProcessor接口,將會(huì)調(diào)用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor經(jīng)常被用作是Bean內(nèi)容的更改,并且由于這個(gè)是在Bean初始化結(jié)束時(shí)調(diào)用After方法,也可用于內(nèi)存或緩存技術(shù)
  • 如果這個(gè)Bean在Spring配置文件中配置了init-method屬性會(huì)自動(dòng)調(diào)用其配置的初始化方法
  • 如果這個(gè)Bean關(guān)聯(lián)了BeanPostProcessor接口,將會(huì)調(diào)用postAfterInitialization(Object obj, String s)方法 注意:以上工作完成以后就可以用這個(gè)Bean了,那這個(gè)Bean是一個(gè)single的,所以一般情況下我們調(diào)用同一個(gè)ID的Bean會(huì)是在內(nèi)容地址相同的實(shí)例
  • 當(dāng)Bean不再需要時(shí),會(huì)經(jīng)過清理階段,如果Bean實(shí)現(xiàn)了DisposableBean接口,會(huì)調(diào)用其實(shí)現(xiàn)的destroy方法
  • 最后,如果這個(gè)Bean的Spring配置中配置了destroy-method屬性,會(huì)自動(dòng)調(diào)用其配置的銷毀方法

6. Bean的作用域

  • singleton(默認(rèn)): 在Spring的IoC容器中只存在一個(gè)對(duì)象實(shí)例,所有該對(duì)象的引用都共享這個(gè)實(shí)例。Spring 容器只會(huì)創(chuàng)建該bean定義的唯一實(shí)例,這個(gè)實(shí)例會(huì)被保存到緩存中,并且對(duì)該bean的所有后續(xù)請(qǐng)求和引用都將返回該緩存中的對(duì)象實(shí)例,一般情況下,無狀態(tài)的bean使用該scope。
  • prototype:每次對(duì)該bean的請(qǐng)求都會(huì)創(chuàng)建一個(gè)新的實(shí)例,一般情況下,有狀態(tài)的bean使用該scope。
  • request:每次http請(qǐng)求將會(huì)有各自的bean實(shí)例,類似于prototype。
  • session:在一個(gè)http session中,一個(gè)bean定義對(duì)應(yīng)一個(gè)bean實(shí)例。
  • global session:在一個(gè)全局的http session中,一個(gè)bean定義對(duì)應(yīng)一個(gè)bean實(shí)例。典型情況下,僅在使用portlet context的時(shí)候有效。

7. AOP

實(shí)現(xiàn)原理:默認(rèn),接口基于JDK動(dòng)態(tài)代理,類為cglib

注意事項(xiàng)

  • 嵌套失效問題
  • final類直接報(bào)錯(cuò)問題(動(dòng)態(tài)代理的實(shí)現(xiàn)原理)

8. Spring MVC

請(qǐng)求過程

  • 用戶請(qǐng)求DispatchServlet
  • DispatchServlet根據(jù)請(qǐng)求路徑調(diào)用具體HandlerMapping返回一個(gè)HandlerExcutionChain
  • DispatchServlet調(diào)用HandlerAdapter適配器
  • HandlerAdapter調(diào)用具體的Handler處理業(yè)務(wù)
  • Handler處理結(jié)束返回一個(gè)具體的ModelAndView給適配器
  • 適配器將ModelAndView給DispatchServlet
  • DispatchServlet把視圖名稱給ViewResolver視圖解析器
  • ViewResolver返回一個(gè)具體的視圖給DispatchServlet
  • 渲染視圖,展示給用戶

二. JVM

1. 內(nèi)存劃分

JVM規(guī)范,將內(nèi)存分為 程序計(jì)數(shù)器、Java棧,也叫虛擬機(jī)棧、本地方法棧、方法區(qū)、堆

  • 程序計(jì)數(shù)器 程序指令保存,從當(dāng)前指令到下一個(gè)指令,從程序計(jì)數(shù)器獲取下一個(gè)指令的地址,直到執(zhí)行所有的指令;線程私有
  • Java棧(虛擬機(jī)棧) 保存方法棧幀,當(dāng)調(diào)用一個(gè)方法,則新創(chuàng)建一個(gè)棧幀,當(dāng)前的方法始終保持在棧幀的頂部;線程私有
  • 本地方法棧 保存本地方法棧幀,當(dāng)調(diào)用一個(gè)本地方法,則新創(chuàng)建一個(gè)棧幀,當(dāng)前的方法始終保持在棧幀的頂部;線程私有
  • 方法區(qū) 保存類信息、靜態(tài)常量、常量;線程共享
  • 堆 保存對(duì)象,最最主要的垃圾收集之處;線程共享

2. 對(duì)象存活

  • 引用計(jì)數(shù)算法:對(duì)對(duì)象引用進(jìn)行計(jì)數(shù),引用則計(jì)數(shù)器 +1,引用失效 -1;計(jì)數(shù)為 0 時(shí)候認(rèn)為不在引用,可以進(jìn)行回收;不能處理對(duì)象循環(huán)引用問題
  • 可達(dá)性分析算法:主要采取此方式,采用虛擬機(jī)棧、類引用對(duì)象、常量對(duì)象、本地方法引用對(duì)象作為根,判斷對(duì)象到根是否存在引用關(guān)系,不可達(dá)則認(rèn)為不在引用,可以進(jìn)行回收;

3. GC回收算法

  • 標(biāo)記-清除算法:對(duì)要回收的對(duì)象,先進(jìn)行標(biāo)志,后進(jìn)行清除,你懂得;但是,久之,會(huì)存在內(nèi)存不連續(xù),比如,一次垃圾回收,回收了,(0,1)和(0,3)和(0,5)三個(gè)位置,但是沒有回收(0,2)和(0,4),那下次的內(nèi)存,就無法使用(0,1)-(0,5)的連續(xù)空間;
  • 復(fù)制算法:為改進(jìn)上面的內(nèi)存碎片問題而產(chǎn)生,對(duì)內(nèi)存分為兩部分,交替回收其中一部分,存活的對(duì)象復(fù)制到另一部分空間,你懂得;但是,有點(diǎn)浪費(fèi),比如,內(nèi)存分為,(0,1)-(0,3)和(0,3)-(0,5)兩個(gè)部分,某次回收(0,1)-(0,3)空間,將存活對(duì)象拷貝到(0,3)-(0,5),而后在(0,1)-(0,3)分配對(duì)象;
  • 標(biāo)記-整理算法:為改進(jìn)上面的內(nèi)存浪費(fèi)問題而產(chǎn)生,對(duì)要回收的對(duì)象,先進(jìn)行標(biāo)志,后進(jìn)行清除,你懂得,然后,將存活的對(duì)象,進(jìn)行整理,移動(dòng)到邊界位置,似的剩余空間連續(xù);比如,一次垃圾回收,回收了,(0,1)和(0,3)和(0,5)三個(gè)位置,但是沒有回收(0,2)和(0,4),然后,將(0,2)和(0,4)移動(dòng)到(0,1)和(0,2),使得(0,3)-(0,5)的空間連續(xù);

4. 類加載過程

jvm中class類的加載過程,大致分為這幾個(gè)步驟

  • 加載(load)
  • 根據(jù)全類名,加載類的二進(jìn)制字節(jié)流
  • 將字節(jié)流轉(zhuǎn)存方法區(qū)
  • 生成Class對(duì)象作為訪問入口
  • 驗(yàn)證(verify)
  • class文件的格式驗(yàn)證,驗(yàn)證是否符合JVM規(guī)范
  • class中的元數(shù)據(jù)驗(yàn)證,驗(yàn)證是否符合Java規(guī)范
  • class的字節(jié)碼驗(yàn)證,驗(yàn)證數(shù)據(jù)流控制流不會(huì)危害JVM環(huán)境
  • 準(zhǔn)備(prepare)
  • 給變量分配內(nèi)存
  • 初始化零值(比如int默認(rèn)為0,boolean默認(rèn)為false)
  • final變量直接賦值
  • 解析
  • 符號(hào)引用變?yōu)橹苯右?/li>
  • 類、字段、方法、接口方法解析
  • 初始化
  • 初始化變量
  • 構(gòu)造函數(shù)
  • static塊

5. 雙親委派機(jī)制

Java中,大概有三種類型加載器,啟動(dòng)類加載器(Bootstrap)<- 標(biāo)準(zhǔn)擴(kuò)展類加載器(Extension)<- 應(yīng)用程序類加載器(Application )<- 上下文類加載器(Custom),從右到左,盡量父類進(jìn)行加載,當(dāng)父類無法進(jìn)行加載時(shí)候,才會(huì)使用子類進(jìn)行加載

  • 意義
  • 防止同一個(gè)JVM,內(nèi)存中出現(xiàn)兩份class二進(jìn)制字節(jié)碼
  • 加載過程
  • 從已加載的類查找是否已經(jīng)存在,存在不需要再次加載
  • 若不存在,則去parent中查找,存在不需要再次加載
  • 若不存在,遞歸在parent中查找,直到找到為止
  • 若找遍所有parent均不存在,且當(dāng)前加載器已經(jīng)沒有parent加載器,則調(diào)用當(dāng)前類加載器的findClass方法,如果能加載,結(jié)束
  • 如果不能,則遞歸返回child類加載器,繼續(xù)調(diào)用findClass方法,如果能加載,結(jié)束
  • 如果找遍所有child的findClass方法,還是不能加載,則拋出異常
  • 破壞雙親委派機(jī)制
  • 將parent設(shè)為null
  • 重寫load(String,boolean)方法,改變類的查找機(jī)制。

三. 多線程

1. 死鎖的四個(gè)條件

  • 互斥
  • 請(qǐng)求與保持
  • 不剝奪
  • 循環(huán)等待

2. 檢查死鎖

  • Jconsole查看死鎖
  • Jstack查看死鎖

3. volatile

  • JMM
  • 內(nèi)存可見性
  • 防止指令重排序
  • 不能保證原子性

4. synchronized

作用

  • 互斥訪問
  • 內(nèi)存可見性
  • 防止指令重排序

用法

  • 修飾普通方法
  • 修飾靜態(tài)方法
  • 修飾代碼塊

注意點(diǎn)

  • 當(dāng)一個(gè)線程在訪問對(duì)象的 synchronized 方法,因?yàn)閷?duì)象只有一把鎖,其他線程無法獲取該對(duì)象的鎖,所以無法訪問該對(duì)象的其他synchronized實(shí)例方法,但是其他線程還是可以訪問該實(shí)例對(duì)象的其他非synchronized方法
  • 實(shí)現(xiàn)原理為,對(duì)象監(jiān)視器,Monitor

5. volatile vs synchronized vs lock

  • 來源差異:volatile、synchronized為Java關(guān)鍵字; lock是Java類
  • 代價(jià)開銷:volatile不是鎖,代價(jià)最?。?lock是一般基于AQS,相對(duì)比synchronized代價(jià)?。?synchronized代價(jià)最大
  • 簡單性:volatile、synchronized為Java關(guān)鍵字,JVM全權(quán)幫忙維護(hù),只要我們能正確使用,不需要我們太多關(guān)心維護(hù); lock是Java類,有很多方法可以調(diào)用,靈活性最好,但是需要自己控制鎖的獲取、釋放

6. 進(jìn)程間通信

  • 管道pipe
  • 命名管道FIFO
  • 消息隊(duì)列MessageQueue
  • 共享存儲(chǔ)SharedMemory
  • 信號(hào)量Semaphore
  • 套接字Socket
  • 信號(hào) ( sinal )

7. 原子操作類

  • CountDownLatch

    • 某線程需要等待多個(gè)線程執(zhí)行完畢,再執(zhí)行。
    • 實(shí)現(xiàn)多個(gè)線程共同等待,同時(shí)開始執(zhí)行任務(wù),不可重用。(此類似于CyclicBarrier,可重用)
  • CyclicBarrier

    • CyclicBarrier允許一組線程互相等待,直到到達(dá)某個(gè)公共屏障點(diǎn)。
    • 與CountDownLatch不同的是該barrier在釋放等待線程后可以重用,所以稱它為循環(huán)(Cyclic)的屏障
  • CountDownLatch、CyclicBarrier差別

    • CountDownLatch和CyclicBarrier都能夠?qū)崿F(xiàn)線程之間的等待,只不過它們側(cè)重點(diǎn)不同: CountDownLatch一般用于某個(gè)線程A等待若干個(gè)其他線程執(zhí)行完任務(wù)之后,它才執(zhí)行; 而CyclicBarrier一般用于一組線程互相等待至某個(gè)狀態(tài),然后這一組線程再同時(shí)執(zhí)行;
    • CountDownLatch是不能夠重用的,而CyclicBarrier是可以重用的。
  • Semaphore

    • Semaphore是一個(gè)計(jì)數(shù)信號(hào)量,它的本質(zhì)是一個(gè)”共享鎖”。
    • 信號(hào)量維護(hù)了一個(gè)信號(hào)量許可集。線程可以通過調(diào)用acquire()來獲取信號(hào)量的許可;當(dāng)信號(hào)量中有可用的許可時(shí),線程能獲取該許可;否則線程必須等待,直到有可用的許可為止。 線程可以通過release()來釋放它所持有的信號(hào)量許可。
    • Semaphore其實(shí)和鎖有點(diǎn)類似,它一般用于控制對(duì)某組資源的訪問權(quán)限。
  • Exchanger

    • 用于進(jìn)行線程間的數(shù)據(jù)交換。
    • 兩個(gè)線程通過exchange方法交換數(shù)據(jù)
    • 該工具類的線程對(duì)象是成對(duì)的
  • ThreadLocal

    • 每個(gè)Thread 維護(hù)一個(gè) ThreadLocalMap 映射表,這個(gè)映射表的 key 是 ThreadLocal實(shí)例本身,value 是真正需要存儲(chǔ)的 Object。
    • ThreadLocal 本身并不存儲(chǔ)值,它只是作為一個(gè) key 來讓線程從 ThreadLocalMap 獲取 value。值得注意的是圖中的虛線,表示 ThreadLocalMap 是使用 ThreadLocal 的弱引用作為 Key 的,弱引用的對(duì)象在 GC 時(shí)會(huì)被回收。
    • ThreadLocalMap使用ThreadLocal的弱引用作為key,如果一個(gè)ThreadLocal沒有外部強(qiáng)引用來引用它,那么系統(tǒng) GC 的時(shí)候,這個(gè)ThreadLocal勢必會(huì)被回收,這樣一來,ThreadLocalMap中就會(huì)出現(xiàn)key為null的Entry,就沒有辦法訪問這些key為null的Entry的value,如果當(dāng)前線程再遲遲不結(jié)束的話,這些key為null的Entry的value就會(huì)一直存在一條強(qiáng)引用鏈:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value永遠(yuǎn)無法回收,造成內(nèi)存泄漏。
    • ThreadLocal里面使用了一個(gè)存在弱引用的map, map的類型是ThreadLocal.ThreadLocalMap. Map中的key為一個(gè)threadlocal實(shí)例。這個(gè)Map的確使用了弱引用,不過弱引用只是針對(duì)key。每個(gè)key都弱引用指向threadlocal。 當(dāng)把threadlocal實(shí)例置為null以后,沒有任何強(qiáng)引用指向threadlocal實(shí)例,所以threadlocal將會(huì)被gc回收。 但是,我們的value卻不能回收,而這塊value永遠(yuǎn)不會(huì)被訪問到了,所以存在著內(nèi)存泄露。因?yàn)榇嬖谝粭l從current thread連接過來的強(qiáng)引用。只有當(dāng)前thread結(jié)束以后,current thread就不會(huì)存在棧中,強(qiáng)引用斷開,Current Thread、Map value將全部被GC回收。最好的做法是將調(diào)用threadlocal的remove方法,這也是等會(huì)后邊要說的。
    • 使用static的ThreadLocal,延長了ThreadLocal的生命周期,可能導(dǎo)致內(nèi)存泄漏。
    • 分配使用了ThreadLocal又不再調(diào)用get(),set(),remove()方法,那么就會(huì)導(dǎo)致內(nèi)存泄漏,因?yàn)檫@塊內(nèi)存一直存在。

寫在最后

限于篇幅,本文只收錄Spring、JVM、多線程的部分面試題;完整的面試題包含Kafka、Mysql、Tomcat、Docker、Spring、MyBatis、Nginx、Netty、Dubbo、Redis、Netty、Spring cloud、分布式、高并發(fā)、性能調(diào)優(yōu)、微服務(wù)等架構(gòu)技。筆者已經(jīng)整理打包好了!

需要的朋友點(diǎn)擊下方傳送門, 即可免費(fèi)領(lǐng)取面試資料和視頻學(xué)習(xí)資料

傳送門

以下是部分面試題截圖

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,683評(píng)論 1 32
  • 不足的地方請(qǐng)大家多多指正,如有其它沒有想到的常問面試題請(qǐng)大家多多評(píng)論,一起成長,感謝!~ String可以被繼承嗎...
    啟示錄是真的閱讀 3,079評(píng)論 3 3
  • 所有知識(shí)點(diǎn)已整理成app app下載地址 J2EE 部分: 1.Switch能否用string做參數(shù)? 在 Jav...
    侯蛋蛋_閱讀 2,713評(píng)論 1 4
  • Java SE 基礎(chǔ): 封裝、繼承、多態(tài) 封裝: 概念:就是把對(duì)象的屬性和操作(或服務(wù))結(jié)合為一個(gè)獨(dú)立的整體,并盡...
    Jayden_Cao閱讀 2,259評(píng)論 0 8
  • 由于時(shí)間倉促,有些地方未寫完,后面會(huì)繼續(xù)補(bǔ)充.如有不妥之處,歡迎及時(shí)與我溝通. 如果你也是在學(xué)習(xí)java,給你們推...
    分不清java閱讀 2,880評(píng)論 0 15

友情鏈接更多精彩內(nèi)容