『美團網』2019最新社招Java面試題分享——Spring+JVM+多線程

圖片 (93).jpg

一. Spring

1. 談談你對Spring的理解

關鍵點面試

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

2. Spring中用到了哪些設計模式

  • 工廠模式,好比 BeanFactory
  • 代理模式,在Aop實現中用到了JDK的動態代理
  • 單例模式,Bean的建立默認就是單利的

3. IoC的啓動過程

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

4. BeanFactory 和 ApplicationContext 區別

  • 功能,BeanFactory負責讀取bean配置,管理bean的加載,實例化,維護bean之間的依賴關係,負責bean的聲明週期;ApplicationContext除了提供上述BeanFactory所能提供的功能以外,還提供了國際化支持、資源訪問、事件傳遞、隊Web的支持等功能
  • BeanFactroy採用的是延遲加載形式來注入Bean的,即只有在使用到某個Bean時(調用getBean()),纔對該Bean進行加載實例化;而ApplicationContext則相反,它是在容器啓動時,一次性建立了全部的Bean。
  • BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但二者之間的區別是:BeanFactory須要手動註冊,而ApplicationContext則是自動註冊

5. Bean 的生命週期

  • 實例化一個Bean,也就是咱們一般說的new
  • 按照Spring上下文對實例化的Bean進行配置,也就是IOC注入
  • 若是這個Bean實現了BeanNameAware接口,會調用它實現的setBeanName(String beanId)方法,此處傳遞的是Spring配置文件中Bean的ID
  • 若是這個Bean實現了BeanFactoryAware接口,會調用它實現的setBeanFactory(),傳遞的是Spring工廠自己(能夠用這個方法獲取到其餘Bean)
  • 若是這個Bean實現了ApplicationContextAware接口,會調用setApplicationContext(ApplicationContext)方法,傳入Spring上下文,該方式一樣能夠實現步驟4,但比4更好,覺得ApplicationContext是BeanFactory的子接口,有更多的實現方法
  • 若是這個Bean關聯了BeanPostProcessor接口,將會調用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor常常被用做是Bean內容的更改,而且因爲這個是在Bean初始化結束時調用After方法,也可用於內存或緩存技術
  • 若是這個Bean在Spring配置文件中配置了init-method屬性會自動調用其配置的初始化方法
  • 若是這個Bean關聯了BeanPostProcessor接口,將會調用postAfterInitialization(Object obj, String s)方法 注意:以上工做完成之後就能夠用這個Bean了,那這個Bean是一個single的,因此通常狀況下咱們調用同一個ID的Bean會是在內容地址相同的實例
  • 當Bean再也不須要時,會通過清理階段,若是Bean實現了DisposableBean接口,會調用其實現的destroy方法
  • 最後,若是這個Bean的Spring配置中配置了destroy-method屬性,會自動調用其配置的銷燬方法

6. Bean的做用域

  • singleton(默認): 在Spring的IoC容器中只存在一個對象實例,全部該對象的引用都共享這個實例。Spring 容器只會建立該bean定義的惟一實例,這個實例會被保存到緩存中,而且對該bean的全部後續請求和引用都將返回該緩存中的對象實例,通常狀況下,無狀態的bean使用該scope。
  • prototype:每次對該bean的請求都會建立一個新的實例,通常狀況下,有狀態的bean使用該scope。
  • request:每次http請求將會有各自的bean實例,相似於prototype。
  • session:在一個http session中,一個bean定義對應一個bean實例。
  • global session:在一個全局的http session中,一個bean定義對應一個bean實例。典型狀況下,僅在使用portlet context的時候有效。

7. AOP

實現原理:默認,接口基於JDK動態代理,類爲cglib算法

注意事項sql

  • 嵌套失效問題
  • final類直接報錯問題(動態代理的實現原理)

8. Spring MVC

請求過程設計模式

  • 用戶請求DispatchServlet
  • DispatchServlet根據請求路徑調用具體HandlerMapping返回一個HandlerExcutionChain
  • DispatchServlet調用HandlerAdapter適配器
  • HandlerAdapter調用具體的Handler處理業務
  • Handler處理結束返回一個具體的ModelAndView給適配器
  • 適配器將ModelAndView給DispatchServlet
  • DispatchServlet把視圖名稱給ViewResolver視圖解析器
  • ViewResolver返回一個具體的視圖給DispatchServlet
  • 渲染視圖,展現給用戶

二. JVM

1. 內存劃分

JVM規範,將內存分爲 程序計數器、Java棧,也叫虛擬機棧、本地方法棧、方法區、堆緩存

  • 程序計數器 程序指令保存,從當前指令到下一個指令,從程序計數器獲取下一個指令的地址,直到執行全部的指令;線程私有
  • Java棧(虛擬機棧) 保存方法棧幀,當調用一個方法,則新建立一個棧幀,當前的方法始終保持在棧幀的頂部;線程私有
  • 本地方法棧 保存本地方法棧幀,當調用一個本地方法,則新建立一個棧幀,當前的方法始終保持在棧幀的頂部;線程私有
  • 方法區 保存類信息、靜態常量、常量;線程共享
  • 堆 保存對象,最最主要的垃圾收集之處;線程共享

2. 對象存活

  • 引用計數算法:對對象引用進行計數,引用則計數器 +1,引用失效 -1;計數爲 0 時候認爲不在引用,能夠進行回收;不能處理對象循環引用問題
  • 可達性分析算法:主要採起此方式,採用虛擬機棧、類引用對象、常量對象、本地方法引用對象做爲根,判斷對象到根是否存在引用關係,不可達則認爲不在引用,能夠進行回收;

3. GC回收算法

  • 標記-清除算法:對要回收的對象,先進行標誌,後進行清除,你懂得;可是,久之,會存在內存不連續,好比,一次垃圾回收,回收了,(0,1)和(0,3)和(0,5)三個位置,可是沒有回收(0,2)和(0,4),那下次的內存,就沒法使用(0,1)-(0,5)的連續空間;
  • 複製算法:爲改進上面的內存碎片問題而產生,對內存分爲兩部分,交替回收其中一部分,存活的對象複製到另外一部分空間,你懂得;可是,有點浪費,好比,內存分爲,(0,1)-(0,3)和(0,3)-(0,5)兩個部分,某次回收(0,1)-(0,3)空間,將存活對象拷貝到(0,3)-(0,5),然後在(0,1)-(0,3)分配對象;
  • 標記-整理算法:爲改進上面的內存浪費問題而產生,對要回收的對象,先進行標誌,後進行清除,你懂得,而後,將存活的對象,進行整理,移動到邊界位置,似的剩餘空間連續;好比,一次垃圾回收,回收了,(0,1)和(0,3)和(0,5)三個位置,可是沒有回收(0,2)和(0,4),而後,將(0,2)和(0,4)移動到(0,1)和(0,2),使得(0,3)-(0,5)的空間連續;

4. 類加載過程

jvm中class類的加載過程,大體分爲這幾個步驟session

  • 加載(load)
  • 根據全類名,加載類的二進制字節流
  • 將字節流轉存方法區
  • 生成Class對象做爲訪問入口
  • 驗證(verify)
  • class文件的格式驗證,驗證是否符合JVM規範
  • class中的元數據驗證,驗證是否符合Java規範
  • class的字節碼驗證,驗證數據流控制流不會危害JVM環境
  • 準備(prepare)
  • 給變量分配內存
  • 初始化零值(好比int默認爲0,boolean默認爲false)
  • final變量直接賦值
  • 解析
  • 符號引用變爲直接引用
  • 類、字段、方法、接口方法解析
  • 初始化
  • 初始化變量
  • 構造函數
  • static塊

5. 雙親委派機制

Java中,大概有三種類型加載器,啓動類加載器(Bootstrap)<- 標準擴展類加載器(Extension)<- 應用程序類加載器(Application )<- 上下文類加載器(Custom),從右到左,儘可能父類進行加載,當父類沒法進行加載時候,纔會使用子類進行加載多線程

  • 意義
  • 防止同一個JVM,內存中出現兩份class二進制字節碼
  • 加載過程
  • 從已加載的類查找是否已經存在,存在不須要再次加載
  • 若不存在,則去parent中查找,存在不須要再次加載
  • 若不存在,遞歸在parent中查找,直到找到爲止
  • 若找遍全部parent均不存在,且當前加載器已經沒有parent加載器,則調用當前類加載器的findClass方法,若是能加載,結束
  • 若是不能,則遞歸返回child類加載器,繼續調用findClass方法,若是能加載,結束
  • 若是找遍全部child的findClass方法,仍是不能加載,則拋出異常
  • 破壞雙親委派機制
  • 將parent設爲null
  • 重寫load(String,boolean)方法,改變類的查找機制。

三. 多線程

1. 死鎖的四個條件

  • 互斥
  • 請求與保持
  • 不剝奪
  • 循環等待

2. 檢查死鎖

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

3. volatile

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

4. synchronized

做用架構

  • 互斥訪問
  • 內存可見性
  • 防止指令重排序

用法併發

  • 修飾普通方法
  • 修飾靜態方法
  • 修飾代碼塊

注意點app

  • 當一個線程在訪問對象的 synchronized 方法,由於對象只有一把鎖,其餘線程沒法獲取該對象的鎖,因此沒法訪問該對象的其餘synchronized實例方法,可是其餘線程仍是能夠訪問該實例對象的其餘非synchronized方法
  • 實現原理爲,對象監視器,Monitor

5. volatile vs synchronized vs lock

  • 來源差別:volatile、synchronized爲Java關鍵字; lock是Java類
  • 代價開銷:volatile不是鎖,代價最小; lock是通常基於AQS,相對比synchronized代價小; synchronized代價最大
  • 簡單性:volatile、synchronized爲Java關鍵字,JVM全權幫忙維護,只要咱們能正確使用,不須要咱們太多關心維護; lock是Java類,有不少方法能夠調用,靈活性最好,可是須要本身控制鎖的獲取、釋放

6. 進程間通訊

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

7. 原子操做類

  • CountDownLatch

    • 某線程須要等待多個線程執行完畢,再執行。
    • 實現多個線程共同等待,同時開始執行任務,不可重用。(此相似於CyclicBarrier,可重用)
  • CyclicBarrier

    • CyclicBarrier容許一組線程互相等待,直到到達某個公共屏障點。
    • 與CountDownLatch不一樣的是該barrier在釋放等待線程後能夠重用,因此稱它爲循環(Cyclic)的屏障
  • CountDownLatch、CyclicBarrier差異

    • CountDownLatch和CyclicBarrier都可以實現線程之間的等待,只不過它們側重點不一樣: CountDownLatch通常用於某個線程A等待若干個其餘線程執行完任務以後,它才執行; 而CyclicBarrier通常用於一組線程互相等待至某個狀態,而後這一組線程再同時執行;
    • CountDownLatch是不可以重用的,而CyclicBarrier是能夠重用的。
  • Semaphore

    • Semaphore是一個計數信號量,它的本質是一個」共享鎖」。
    • 信號量維護了一個信號量許可集。線程能夠經過調用acquire()來獲取信號量的許可;當信號量中有可用的許可時,線程能獲取該許可;不然線程必須等待,直到有可用的許可爲止。 線程能夠經過release()來釋放它所持有的信號量許可。
    • Semaphore其實和鎖有點相似,它通常用於控制對某組資源的訪問權限。
  • Exchanger

    • 用於進行線程間的數據交換。
    • 兩個線程經過exchange方法交換數據
    • 該工具類的線程對象是成對的
  • ThreadLocal

    • 每一個Thread 維護一個 ThreadLocalMap 映射表,這個映射表的 key 是 ThreadLocal實例自己,value 是真正須要存儲的 Object。
    • ThreadLocal 自己並不存儲值,它只是做爲一個 key 來讓線程從 ThreadLocalMap 獲取 value。值得注意的是圖中的虛線,表示 ThreadLocalMap 是使用 ThreadLocal 的弱引用做爲 Key 的,弱引用的對象在 GC 時會被回收。
    • ThreadLocalMap使用ThreadLocal的弱引用做爲key,若是一個ThreadLocal沒有外部強引用來引用它,那麼系統 GC 的時候,這個ThreadLocal勢必會被回收,這樣一來,ThreadLocalMap中就會出現key爲null的Entry,就沒有辦法訪問這些key爲null的Entry的value,若是當前線程再遲遲不結束的話,這些key爲null的Entry的value就會一直存在一條強引用鏈:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value永遠沒法回收,形成內存泄漏。
    • ThreadLocal裏面使用了一個存在弱引用的map, map的類型是ThreadLocal.ThreadLocalMap. Map中的key爲一個threadlocal實例。這個Map的確使用了弱引用,不過弱引用只是針對key。每一個key都弱引用指向threadlocal。 當把threadlocal實例置爲null之後,沒有任何強引用指向threadlocal實例,因此threadlocal將會被gc回收。 可是,咱們的value卻不能回收,而這塊value永遠不會被訪問到了,因此存在着內存泄露。由於存在一條從current thread鏈接過來的強引用。只有當前thread結束之後,current thread就不會存在棧中,強引用斷開,Current Thread、Map value將所有被GC回收。最好的作法是將調用threadlocal的remove方法,這也是等會後邊要說的。
    • 使用static的ThreadLocal,延長了ThreadLocal的生命週期,可能致使內存泄漏。
    • 分配使用了ThreadLocal又再也不調用get(),set(),remove()方法,那麼就會致使內存泄漏,由於這塊內存一直存在。

寫在最後

限於篇幅,本文只收錄Spring、JVM、多線程的部分面試題;完整的面試題包含Kafka、Mysql、Tomcat、Docker、Spring、MyBatis、Nginx、Netty、Dubbo、Redis、Netty、Spring cloud、分佈式、高併發、性能調優、微服務等架構技。筆者已經整理打包好了!

須要的朋友點擊下方傳送門, 便可免費領取面試資料和視頻學習資料

傳送門

如下是部分面試題截圖

相關文章
相關標籤/搜索