阿里巴巴一面:13道經典面試題解析!


前言

大家好,我是麒麟改bug,最近有不少粉絲去阿里巴巴面試了,回來之后總結不少難題給我,以下是面試的真題,跟大家一起來討論怎么回答。

阿里一面

1、說⼀下ArrayList和LinkedList區別

  1. ⾸先,他們的底層數據結構不同,ArrayList底層是基於數組實現的,LinkedList底層是基於鏈表實現的
  2. 由於底層數據結構不同,他們所適⽤的場景也不同,ArrayList更適合隨機查找,LinkedList更適合刪除和添加,查詢、添加、刪除的時間復雜度不同
  3. 另外ArrayList和LinkedList都實現了List接⼝,但是LinkedList還額外實現了Deque接⼝,所以LinkedList還可以當做隊列來使⽤

2、說一下HashMap的Put方法

  1. 根據Key通過哈希算法與與運算得出數組下標
  2. 如果數組下標位置元素為空,則將key和value封裝為Entry對象(JDK1.7中是Entry對象,JDK1.8中是Node對象)並放⼊該位置
  3. 如果數組下標位置元素不為空,則要分情況討論

a. 如果是JDK1.7,則先判斷是否需要擴容,如果要擴容就進⾏擴容,如果不⽤擴容就⽣成Entry對象,並使⽤頭插法添加到當前位置的鏈表中 b. 如果是JDK1.8,則會先判斷當前位置上的Node的類型,看是紅⿊樹Node,還是鏈表Node

  • i. 如果是紅⿊樹Node,則將key和value封裝為⼀個紅⿊樹節點並添加到紅⿊樹中去,在這個過程中會判斷紅⿊樹中是否存在當前key,如果存在則更新value
  • ii. 如果此位置上的Node對象是鏈表節點,則將key和value封裝為⼀個鏈表Node並通過尾插法插⼊到鏈表的最后位置去,因為是尾插法,所以需要遍歷鏈表,在遍歷鏈表的過程中會判斷是否存在當前key,如果存在則更新value,當遍歷完鏈表后,將新鏈表Node插⼊到鏈表中,插⼊到鏈表后,會看當前鏈表的節點個數,如果⼤於等於8,那么則會將該鏈表轉成紅⿊樹
  • iii. 將key和value封裝為Node插⼊到鏈表或紅⿊樹中后,再判斷是否需要進⾏擴容,如果需要就擴容,如果不需要就結束PUT⽅法

3、說一下ThreadLocal

  1. ThreadLocal是Java中所提供的線程本地存儲機制,可以利⽤該機制將數據緩存在某個線程內部,該線程可以在任意時刻、任意⽅法中獲取緩存的數據
  2. ThreadLocal底層是通過ThreadLocalMap來實現的,每個Thread對象(注意不是ThreadLocal對象)中都存在⼀個ThreadLocalMap,Map的key為ThreadLocal對象,Map的value為需要緩存的值
  3. 如果在線程池中使⽤ThreadLocal會造成內存泄漏,因為當ThreadLocal對象使⽤完之后,應該要把設置的key,value,也就是Entry對象進⾏回收,但線程池中的線程不會回收,⽽線程對象是通過強引⽤指向ThreadLocalMap,ThreadLocalMap也是通過強引⽤指向Entry對象,線程不被回收,Entry對象也就不會被回收,從⽽出現內存泄漏,解決辦法是,在使⽤了ThreadLocal對象之后,⼿動調⽤ThreadLocal的remove⽅法,⼿動清楚Entry對象
  4. ThreadLocal經典的應⽤場景就是連接管理(⼀個線程持有⼀個連接,該連接對象可以在不同的⽅法之間進⾏傳遞,線程之間不共享同⼀個連接)

5、說一下JVM中,哪些是共享區,哪些可以作為gc root你們項目

1、堆區和⽅法區是所有線程共享的,棧、本地⽅法棧、程序計數器是每個線程獨有的


2、什么是gc root,JVM在進⾏垃圾回收時,需要找到“垃圾”對象,也就是沒有被引⽤的對象,但是直接找“垃圾”對象是⽐較耗時的,所以反過來,先找“⾮垃圾”對象,也就是正常對象,那么就需要從某些“根”開始去找,根據這些“根”的引⽤路徑找到正常對象,⽽這些“根”有⼀個特征,就是它只會引⽤其他對象,⽽不會被其他對象引⽤,例如:棧中的本地變量、⽅法區中的靜態變量、本地⽅法棧中的變量、正在運⾏的線程等可以作為gc root。

6、你們項目如何排查JVM問題

對於還在正常運⾏的系統:

  1. 可以使⽤jmap來查看JVM中各個區域的使⽤情況

  2. 可以通過jstack來查看線程的運⾏情況,⽐如哪些線程阻塞、是否出現了死鎖

  3. 可以通過jstat命令來查看垃圾回收的情況,特別是fullgc,如果發現fullgc⽐較頻繁,那么就得進⾏調優了

  4. 通過各個命令的結果,或者jvisualvm等⼯具來進⾏分析

  5. ⾸先,初步猜測頻繁發送fullgc的原因,如果頻繁發⽣fullgc但是⼜⼀直沒有出現內存溢出,那么表示fullgc實際上是回收了很多對象了,所以這些對象最好能在younggc過程中就直接回收掉,避免這些對象進⼊到⽼年代,對於這種情況,就要考慮這些存活時間不⻓的對象是不是⽐較⼤,導致年輕代放不下,直接進⼊到了⽼年代,嘗試加⼤年輕代的⼤⼩,如果改完之后,fullgc減少,則證明修改有效

  6. 同時,還可以找到占⽤CPU最多的線程,定位到具體的⽅法,優化這個⽅法的執⾏,看是否能避免某些對象的創建,從⽽節省內存

對於已經發⽣了OOM的系統:

  1. ⼀般⽣產系統中都會設置當系統發⽣了OOM時,⽣成當時的dump⽂件
(- XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/base) 
  1. 我們可以利⽤jsisualvm等⼯具來分析dump⽂件

  2. 根據dump⽂件找到異常的實例對象,和異常的線程(占⽤CPU⾼),定位到具體的代碼

  3. 然后再進⾏詳細的分析和調試

總之,調優不是⼀蹴⽽就的,需要分析、推理、實踐、總結、再分析,最終定位到具體的問題

7、如何查看線程死鎖

1.可以通過jstack命令來進⾏查看,jstack命令中會顯示發⽣了死鎖的線程

2.或者兩個線程去操作數據庫時,數據庫發⽣了死鎖,這是可以查詢數據庫的死鎖情況

1、查詢是否鎖表 
show OPEN TABLES where In_use > 0;
2、查詢進程 
show processlist;
3、查看正在鎖的事務 
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
4、查看等待鎖的事務 
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;

阿里面試真題解析|配套筆記:

8、線程之間如何進行通訊的

  1. 線程之間可以通過共享內存或基於⽹絡來進⾏通信

  2. 如果是通過共享內存來進⾏通信,則需要考慮並發問題,什么時候阻塞,什么時候喚醒

  3. 像Java中的wait()、notify()就是阻塞和喚醒

  4. 通過⽹絡就⽐較簡單了,通過⽹絡連接將通信數據發送給對⽅,當然也要考慮到並發問題,處理⽅式就是加鎖等⽅式

9、介紹一下Spring,讀過源碼介紹一下大致流程

  1. Spring是⼀個快速開發框架,Spring幫助程序員來管理對象

  2. Spring的源碼實現的是⾮常優秀的,設計模式的應⽤、並發安全的實現、⾯向接⼝的設計等

  3. 在創建Spring容器,也就是啟動Spring時:

a. ⾸先會進⾏掃描,掃描得到所有的BeanDefinition對象,並存在⼀個Map中
b. 然后篩選出⾮懶加載的單例BeanDefinition進⾏創建Bean,對於多例Bean不需要在啟動過程中去進⾏創建,對於多例Bean會在每次獲取Bean時利⽤BeanDefinition去創建
c. 利⽤BeanDefinition創建Bean就是Bean的創建⽣命周期,這期間包括了合並BeanDefinition、推斷構造⽅法、實例化、屬性填充、初始化前、初始化、初始化后等步驟,其中AOP就是發⽣在初始化后這⼀步驟中

  1. 單例Bean創建完了之后,Spring會發布⼀個容器啟動事件

  2. Spring啟動結束

  3. 在源碼中會更復雜,⽐如源碼中會提供⼀些模板⽅法,讓⼦類來實現,⽐如源碼中還涉及到⼀些BeanFactoryPostProcessor和BeanPostProcessor的注冊,Spring的掃描就是通過BenaFactoryPostProcessor來實現的,依賴注⼊就是通過BeanPostProcessor來實現的

  4. 在Spring啟動過程中還會去處理@Import等注解

10、說一下Spring的事務機制

  1. Spring事務底層是基於數據庫事務和AOP機制的

  2. ⾸先對於使⽤了@Transactional注解的Bean,Spring會創建⼀個代理對象作為Bean

  3. 當調⽤代理對象的⽅法時,會先判斷該⽅法上是否加了@Transactional注解

  4. 如果加了,那么則利⽤事務管理器創建⼀個數據庫連接

  5. 並且修改數據庫連接的autocommit屬性為false,禁⽌此連接的⾃動提交,這是實現Spring事務⾮常重要的⼀步

  6. 然后執⾏當前⽅法,⽅法中會執⾏sql

  7. 執⾏完當前⽅法后,如果沒有出現異常就直接提交事務

  8. 如果出現了異常,並且這個異常是需要回滾的就會回滾事務,否則仍然提交事務

  9. Spring事務的隔離級別對應的就是數據庫的隔離級別

  10. Spring事務的傳播機制是Spring事務⾃⼰實現的,也是Spring事務中最復雜的

  11. Spring事務的傳播機制是基於數據庫連接來做的,⼀個數據庫連接⼀個事務,如果傳播機制配置為需要新開⼀個事務,那么實際上就是先建⽴⼀個數據庫連接,在此新數據庫連接上執⾏sql

11、什么時候@Transactional失效

因為Spring事務是基於代理來實現的,所以某個加了@Transactional的⽅法只有是被代理對象調⽤時,那么這個注解才會⽣效,所以如果是被代理對象來調⽤這個⽅法,那么@Transactional是不會⽣效的。

同時如果某個⽅法是private的,那么@Transactional也會失效,因為底層cglib是基於⽗⼦類來實現的,⼦類是不能重載⽗類的private⽅法的,所以⽆法很好的利⽤代理,也會導致@Transactianal失效

Dubbo底層是通過RPC來完成服務和服務之間的調⽤的,Dubbo⽀持很多協議,⽐如默認的dubbo協議,⽐如http協議、⽐如rest等都是⽀持的,他們的底層所使⽤的技術是不太⼀樣的,⽐如dubbo協議底層使⽤的是netty,也可以使⽤mina,http協議底層使⽤的tomcat或jetty。

服務消費者在調⽤某個服務時,會將當前所調⽤的服務接⼝信息、當前⽅法信息、執⾏⽅法所傳⼊的⼊參信息等組裝為⼀個Invocation對象,然后不同的協議通過不同的數據組織⽅式和傳輸⽅式將這個對象傳送給服務提供者,提供者接收到這個對象后,找到對應的服務實現,利⽤反射執⾏對應的⽅法,得到⽅法結果后再通過⽹絡響應給服務消費者。

當然,Dubbo在這個調⽤過程中還做很多其他的設計,⽐如服務容錯、負載均衡、Filter機制、動態路由機制等等,讓Dubbo能處理更多企業中的需求。

12、Dubbo是如何做系統交互的Dubbo的負載均衡策略

Dubbo⽬前⽀持:

  1. 平衡加權輪詢算法

  2. 加權隨機算法

  3. ⼀致性哈希算法

  4. 最⼩活躍數算法

13、還讀過哪些框架源碼介紹一下你還熟悉的

這個問題⽐較⼴泛,你即可以說:HashMap、線程池等JDK⾃帶的源碼,也可以說Mybatis、Spring Boot、Spring Cloud、消息隊列等開發框架或中間件的源碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM