還有一篇可以看看:隨筆分類 - 備戰阿里
他媽 辛辛苦苦准備了2個月,真正的去阿里面試的時候,簡直就是打臉大會啊,面試官的問題都聽不明白,回答更是天方夜譚。。。。。。
以下是本人 2019年面試阿里的題目:
自我介紹環節:
問題1:上來就講細節,其實對方根本聽不懂,應該先介紹項目背景,如果對方感興趣的話,會多問幾句,不要自己上來就說細節。除非對方明確要求問。
問題2:對於自己項目的架構圖一定要牢記,面試一般都會讓你畫一下架構圖,最好漂亮點,去網上查查名詞,吹一吹,唬唬人,多做點功課,因為這個可以說是送分題,哪個面試官都會問,准備一定用的上的。
問題3:對於項目建議美團的項目要重點寫,其他的簡單點寫,這樣讓面試官問起來好問點,不然只能問自我評價的東西了。
最重要的源碼一定要看點,
這兩天阿里面試官問了我幾個問題。
3、MYSQL數據庫隔離級別:
Mysql的隔離級別默認是 可重復讀,REPEAT-READ
Oracle和SQL Server的默認隔離級別是:read-commit;
MySQL數據庫為我們提供的四種隔離級別:
數據庫事務的四大特性以及4種事務的隔離級別-以及對應的5種JDBC事務隔離級別
4、IO復用,什么是IO復用,以及應用
這個問題也比較復雜:
另一個面試官還問到IO與NIO的區別:
5、數據庫遇到性能問題如何處理,
(1) 硬件提升:容量不足,磁盤不足,使用share memory
(2) 軟件提升:性能不夠,可以使用分庫,分表,讀寫分離
(3)分布式部署:一個事務,兩個數據庫聯動。使用分布式事務。
分布式部署問題:
1、(a)一致性Hash算法解釋
答:這是dubbo 的負載均衡的算法:ConsistentHash LoadBalance:一致性Hash策略,具體配置方法可以參考Dubbo文檔。相同調用參數的請求會發送到同一個服務提供方節點上,如果某個節點發生故障無法提供服務,則會基於一致性Hash算法映射到虛擬節點上(其他服務提供方)
(b) 阿里還問 如果有7台服務器給你提供接口,服務器如何找到這個接口的,其實考察的是負載均衡算法:
Dubbo框架內置提供了4種負載均衡策略,如下所示:
(1)Random LoadBalance:隨機策略,配置值為random。可以設置權重,有利於充分利用服務器的資源,高配的可以設置權重大一些,低配的可以稍微小一些
(2)RoundRobin LoadBalance:輪詢策略,配置值為roundrobin。
(3)LeastActive LoadBalance:配置值為leastactive。根據請求調用的次數計數,處理請求更慢的節點會受到更少的請求
(4)ConsistentHash LoadBalance:一致性Hash策略,具體配置方法可以參考Dubbo文檔。相同調用參數的請求會發送到同一個服務提供方節點上,如果某個節點發生故障無法提供服務,則會基於一致性Hash算法映射到虛擬節點上(其他服務提供方)
具體參考:Dubbo學習(二) Dubbo 集群容錯模式-負載均衡模式
2、CAP理論
參考:CAP原則 (阿里)
3、原子操作 AtomicInteger
對CAS的理解,CAS是一種無鎖算法,CAS有3個操作數,內存值V,舊的預期值A,要修改的新值B。當且僅當預期值A和內存值V相同時,將內存值V修改為B,否則什么都不做。
4、分布式如何解決一致性問題
6、高並發綜合策略
7、我說用過docker ,他問 給你5台服務器 如何部署docker,自動化 如何寫腳本 使用 Swarm ,compose;
參考:docker swarm和compose 的使用(阿里)
8 docker網牆原理;
9、如果數據量很大,如何在web層面處理這些大的數據。
10、當處理上億級別的數據的時候,在web層如何搭建架構:
參考:【系統架構】億級Web系統搭建(1):Web負載均衡(阿里)
二、數據庫問題:
1、redis
(a)Redis的內存廢棄策略
(b)redis 的網絡架構,單線程還能實現並發量這么高,如何實現的,為何redis使用了跳表,沒有使用B+數,因為B+樹是為了減少IO,而redis 是在內存里面,所以不用B+樹
具體參考:redis為何單線程 效率還這么高 為何使用跳表不使用B+樹做索引(阿里)
2、redis高並發的key怎么處理
參考:高並發架構系列:Redis並發競爭key的解決方案詳解
3、redis 緩存雪崩(大量的key同時失效) 緩存擊穿如何處理:
參考:Redis緩存穿透、緩存雪崩、redis並發問題 並發競爭key的解決方案 (阿里)
4、redis 是如何找到其中的key的; mysql是如何通過索引找到key 並刪除的
這其實考察的是索引的原理
Mysql 使用了B+Tree 具體參考:一步步分析為什么B+樹適合作為索引的結構
redis 使用了跳表 復雜度O(logn) 參考:聊聊Mysql索引和redis跳表 ---redis的跳表原理 時間復雜度O(logn)(阿里)
5、mysql 的innoDB使用的是B+Tree索引,mysiam呢使用了什么索引(fulltext索引);
舉例來說,比如我在orderId上面 添加了索引,如果 我執行 delete tb_order where orderId='12';
mysql 如何通過index定位到這條數據,並刪除的。
答: fulltext索引(全文索引) 僅可用於 MyISAM 表 ,
在MySQL中,主要有四種類型的索引,分別為:B-Tree索引,Hash索引,Fulltext索引和R-Tree索引
具體參考:一步步分析為什么B+樹適合作為索引的結構
區別參考:MySql的多存儲引擎架構, 默認的引擎InnoDB與 MYISAM的區別(滴滴 阿里)
5、HIVE sql 調優
三、Sring MVC 的請求過程,一個Controller是單例還是多實例(答案:默認是單例的)
參考:Spring學習 6- Spring MVC (Spring MVC原理及配置詳解)
四、IO 通訊
1、netty原理
參考:新手入門:目前為止最透徹的的Netty高性能原理和框架架構解析
2、BIO,NIO,AIO的原理
參考:IO復用,AIO,BIO,NIO,同步,異步,阻塞和非阻塞 區別(百度)
3、Websocket連接池 原理,如何實現一個端口 實現多個並發請求的。用了哪一種連接池。如何你來設計連接池,你會考慮哪些參數,比如 過期時間等
我們使用了 GenericObjectPool 一般對象池技術
@Override @RhinoBreaker @ApiResult public CityResponse execRequest(CityRequest cityRequest) { GenericObjectPool<WSClient> wsClientPool = null; WSClient wsClient = null; URI uri; Transaction transaction = Cat.newTransaction(CatConstant.TRANSACTION_WEBSOCKET_REQUEST.getKey(), CatConstant.TRANSACTION_WEBSOCKET_REQUEST.getValue()); try { uri = getURI(cityRequest); wsClientPool = wsClientPoolCache.get(uri.toString()); wsClient = wsClientPool.borrowObject(); CityResponse cityResponse = send(wsClient, Collections.singletonList(cityRequest)); if (cityResponse.isOk()) { transaction.setSuccessStatus(); } else { transaction.setStatus("上報失敗"); } return cityResponse; } catch (Exception e) { transaction.setStatus(e); /*網絡IO 異常 & 可重試異常 & 連接池獲取異常*/ return new FailureResponse(e); } finally { if (wsClientPool != null && wsClient != null) { ((WebSocketImpl) wsClient.getConnection()).updateLastPong(); wsClientPool.returnObject(wsClient); } transaction.complete(); } }
舉例說一下 連接池丟棄的參數寫法。關鍵字
連接池丟棄的寫法:
.removalListener((RemovalListener<String, GenericObjectPool<WSClient>>) removalNotification -> { /* 連接池銷毀 */ if (removalNotification.getValue() != null) { removalNotification.getValue().close(); LOGGER.warn("url:{} 的WebSocket連接池已銷毀,池數目:{}", removalNotification.getKey(), wsClientPoolCache.size()); } })
線程池丟棄最開始的數據的寫法
這是定義:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.acc = System.getSecurityManager() == null ? null : AccessController.getContext(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
下面是重寫:
}, (r, executor) -> { if (!executor.isShutdown()) { /* 丟棄隊列最老的數據 */ if (executor.getQueue().poll() != null) { Cat.logMetricForCount(CatConstant.METRIC_DISCARD_FILE_TASK_COUNT); } executor.execute(r); } });
4、網絡中斷 報錯 如何處理
5、HTTP協議的架構,http1.0,http1.1,http2.0協議的區別和聯系。
參考:HTTP/1.0和HTTP/1.1 http2.0的區別,HTTP怎么處理長連接(阿里)
五、MQ
1、kafka 如何處理高並發的,比如一條數據進入partition之后,如何找到這條數據的。怎么消費的,消費數據的,
參考:kafka如何實現高並發存儲-如何找到一條需要消費的數據(阿里)
六、數據結構:
1、hashmap的時間復雜度O(1),數組的是 O(n),鏈表也是 O(n)
參考:HashMap, HashTable,HashSet,TreeMap 的時間復雜度 注意數組鏈表 增刪改查的時間復雜度都不相同(阿里)
2、Hashtable繼承自Dictionary類,而HashMap繼承自AbstractMap類。但二者都實現了Map接口。hashtable的初始大小
hashmap的初始大小,為什么擴容因子是0.75; 以及resize的操作原理 entry 擴容完了之后,順序會顛倒嗎?
3、冒泡排序的時間復雜度是多少,空間復雜度又是多少?
答:空間復雜度O(1)
4、blockQueue的長度是多少。
5、hashMap的初始大小 16 擴容因子0.75, 原因?
參考:HashMap默認加載因子為什么選擇0.75?(阿里)
(a) ArrayList初始化n=10個空間擴容(n3)/2 + 1,如果不夠設置傳入的值
(b) HashMap初始化n=16空間擴容2n,在並發環境下,可能會形成環狀鏈表(擴容時可能造成)
(c) Hashtable初始化n=11空間擴容2n+1
jdk1.6ConcurrentHashMap初始化segments=16個空間每個segments是初始化一個HashEntry 擴容segments=n2
jdk1.7ConcurrentHashMap初始化segments=16個空間每個segments是初始化兩個HashEntry 擴容segments=n*2
6、 concurrentHashMap 是否是線程安全的,如何實現的?
我做了總結:使用了分段鎖的技術
這個問題比較復雜,可以參考:
hashmap,hashTable concurrentHashMap 是否為線程安全,區別,如何實現的
而且Java1.7與Java1.8實現方式並不相同,阿里的面試官3個都問道這個問題,他們好這口,
我整理了一下:ConcurrentHashMap原理分析(1.7與1.8)
7、hashmap 如果多線程操作的話,線程不安全,為什么會不安全:
答案:多線程put的時候,會引起死循環,好好看看這個例子,有圖有解釋,很管用
具體原因參考:HashMap多線程並發問題分析-正常和異常的rehash1(阿里)
7、紅黑樹的插入和刪除操作,紅黑樹的樹高,左旋右旋,用紅黑樹實現currentHashMap,紅黑樹與B+樹的區別
紅黑樹的樹高度<=2log(n+1) 時間復雜度:O(lgn)
紅黑樹: R-B Tree,全稱是Red-Black Tree,又稱為“紅黑樹”,它一種特殊的二叉查找樹。紅黑樹的每個節點上都有存儲位表示節點的顏色,可以是紅(Red)或黑(Black)。
(4)紅黑樹的特性:
(1)每個節點或者是黑色,或者是紅色。
(2)根節點是黑色。
(3)每個葉子節點(NIL)是黑色。 [注意:這里葉子節點,是指為空(NIL或NULL)的葉子節點!]
(4)如果一個節點是紅色的,則它的子節點必須是黑色的。
(5)從一個節點到該節點的子孫節點的所有路徑上包含相同數目的黑節點。
紅黑樹特征,以及新增刪除 參考:紅黑樹之 原理和算法詳細介紹(阿里面試-treemap使用了紅黑樹) 紅黑樹的時間復雜度是O(lgn) 高度<=2log(n+1)1、X節點左旋-將X右邊的子節點變成 父節點 2、X節點右旋-將X左邊的子節點變成父節點
8、如果一個數據結構,需要進行深度優先的遍歷,或者進行廣度優先的遍歷,應該首先考慮什么數據結構來處理
答案:
深度優先-棧
廣度優先-隊列
因為深度優先需要無路可走時按照來路往回退,正好是后進先出 就是棧
廣度優先則需要保證先訪問頂點的未訪問鄰接點先訪問,恰好就是先進先出那就是 隊列
六、Lock 鎖問題;
1、synchronized 如何實現 線程安全的
2、膨脹鎖的原理。
七、JVM內存問題:
1、強引用 和軟引用,弱引用(比如threadlocal),虛引用,區別,可達性算法的定義;
在JDK 1.2之后,Java對引用的概念進行了擴充,將引用分為強引用(Strong Reference)、軟引用 依次逐漸減弱。 強引用 在程序代碼中普遍存在的,類似 Object obj = new Object() 這類引用,只要強引用還存在,垃圾收集器永遠不會回收掉被引用的對象。 軟引用 用來描述一些還有用但並非必須的對象。對於軟引用關聯着的對象,在系統將要發生內存溢出異常之前,將會把這些對象列進回收范圍之中進行第二次回收。如果這次回收后還沒有足夠的內存,才會拋出內存溢出異常。 弱引用 也是用來描述非必需對象的,但是它的強度比軟引用更弱一些,被弱引用關聯的對象只能生存到下一次垃圾收集發生之前。當垃圾收集器工作時,無論當前內存是否足夠,都會回收掉只被弱引用關聯的對象。在JDK 1.2之后,提供了WeakReference類來實現弱引用。比如 threadlocal 虛引用 也叫幽靈引用或幻影引用(名字真會取,很魔幻的樣子),是最弱的一種引用 關系。一個對象是否有虛引用的存在,完全不會對其生存時間構成影響,也無法通過虛引用來取得一個對象實例。它的作用是能在這個對象被收集器回收時收到一個系統通知。。在JDK 1.2之后,提供了PhantomReference類來實現虛引用。
2、垃圾回收算法:
首先問一下目前你們用的垃圾回收機制是什么樣的:
回答:使用命令:
java -XX:+PrintFlagsFinal -version | grep :
就能看到:
uintx InitialHeapSize := 128981632 {product} uintx MaxHeapSize := 2065694720 {product} bool PrintFlagsFinal := true {product} bool UseCompressedOops := true {lp64_product} bool UseParallelGC := true {product} java version "1.7.0_76" Java(TM) SE Runtime Environment (build 1.7.0_76-b13) Java HotSpot(TM) 64-Bit Server VM (build 24.76-b04, mixed mode)
里面能看到我的來及回收機制是 ParallelGC的垃圾回收機制;具體參考:JVM的垃圾回收機制 總結(垃圾收集、回收算法、垃圾回收器)
能否說幾個G1的配置參數:參考:
回答:比如用戶期望的最大停頓時間
-XX:MaxGCPauseMillis=200
或者使用 G1的命令:-XX:+UseG1GC
具體參考:G1相關參數
(1)CMS與G1的區別和聯系
主要的區別:G1只有的並發標記的時候才不會stop-the-world 其他步驟都會stop-the-world;
不要以為掌握到這個程度就行了,看看阿里下面的問題:
(2)G1垃圾回收算法的架構,跟分代收集很不一樣,架構是怎樣的,還有就是G1如何做到時間可控的
(a) G1雖然也把內存分成了這三大類,Eden(E), Suvivor(S)和Old(O), 但是在G1里面這三大類不是涇渭分明的三大塊內存,G1把內存划分成很多小塊, 每個小塊會被標記為E/S/O中的一個,可以前面一個是Eden后面一個就變成Survivor了。
(b) G1其實是Garbage First的意思,垃圾優先? 不是,是優先處理那些垃圾多的內存塊的意思。
(c)G1的另一個顯著特點他能夠讓用戶設置應用的暫停時間,為什么G1能做到這一點呢?也許你已經注意到了,G1回收的第4步,它是“選擇一些內存塊”,而不是整代內存來回收,這是G1跟其它GC非常不同的一點,其它GC每次回收都會回收整個Generation的內存(Eden, Old), 而回收內存所需的時間就取決於內存的大小,以及實際垃圾的多少,所以垃圾回收時間是不可控的;而G1每次並不會回收整代內存,到底回收多少內存就看用戶配置的暫停時間,配置的時間短就少回收點,配置的時間長就多回收點,伸縮自如。
更具體的參考:G1 垃圾收集器架構和如何做到可預測的停頓(阿里)
G1與CMS的區別參考:CMS收集器和G1收集器 他們的優缺點對比 G1只有並發標記才不會stop-the-world 其他都會停下來(阿里多次問到)
3、當new 一個對象的時候,JVM做了那些事情。還有我們在堆分配了空間,那么會不會線程不安全,因為堆是共享的?(阿里面試的高頻問題,問了至少3次了)
回答:首先一定是安全的,至於原因:為了保證Java對象的內存分配的安全性,同時提升效率,一般有兩種解決方案:
- 1、對分配內存空間的動作做同步處理,采用CAS機制,配合失敗重試的方式保證更新操作的線程安全性。
- 2、每個線程在Java堆中預先分配一小塊內存,然后再給對象分配內存的時候,直接在自己這塊"私有"內存中分配,當這部分區域用完之后,再分配新的"私有"內存。
-
方案1在每次分配時都需要進行同步控制,這種是比較低效的。
方案2是HotSpot虛擬機中采用的,這種方案被稱之為TLAB分配,即Thread Local Allocation Buffer。這部分Buffer是從堆中划分出來的,但是是本地線程獨享的。
這里值得注意的是,我們說TLAB時線程獨享的,但是只是在“分配”這個動作上是線程獨占的,至於在讀取、垃圾回收等動作上都是線程共享的。而且在使用上也沒有什么區別
-
不知道大家有沒有想過,我們使用了TLAB之后,在TLAB上給對象分配內存時線程獨享的了,這就沒有沖突了,但是,TLAB這塊內存自身從堆中划分出來的過程也可能存在內存安全問題啊。所以,在對於TLAB的分配過程,還是需要進行同步控制的。但是這種開銷相比於每次為單個對象划分內存時候對進行同步控制的要低的多。
參考:靈魂拷問:Java對象的內存分配過程是如何保證線程安全的?(阿里面試)
4、 如果遇到了Full GC如何處理,
首先搞明白什么事Full GC ,JVM回收主要是堆的回收,而堆分為 新生代和老年代,新生代如果滿了就會執行Minor GC,回收一次新生代的內存,
新生代執行垃圾回收很頻繁,因此使用了復制法(Coping)
存活的對象會放在老年代,如果老年代滿了,會執行Full GC,回收老年代的垃圾不是很頻繁,因此使用了標記整理法(Mark-Compact)
由此看來:Full GC的生成條件:
(a)調用System.gc時,系統建議執行Full GC,但是不必然執行
(b)老年代空間不足
(c)方法區空間不足
(d)通過Minor GC后進入老年代的平均大小大於老年代的可用內存
(e)由Eden區、From Space區向To Space區復制時,對象大小大於To Space可用內存,則把該對象轉存到老年代,且老年代的可用內存小於該對象大小
阿里大神面試官回答:
(a)看一下有沒有大對象
(b)大的靜態對象
(c) Dump 內存快照,進行分析
八、線上問題排查:
1、如果一台服務器負載很高,如何處理 ,假如他cpu使用率很低,IO也很低呢?
不要上來就說 看一下 是CPU高還是IO高,因為有時候,CPU不高,IO也不高,但服務器負載就是高,看下面的博客
參考:服務器負載過高問題分析-不是cpu高負載也不是IO負載如何處理(阿里)
九、算法題目
1、給你1000條數據,每條數據都帶有起始結束時間的任務,如何把時間重疊的數據取出來;
2、給你1億個數據,里面有重復的,如何把前100最小的數取出來
參考:關於“100g文件全是數組,取最大的100個數”解決方法匯總
十、Cache 緩存
1、guava如何刪除過期數據,guava的架構,如何實現的刪除,都要看源碼,別只看博客解說。就是應付也要看點,不然怎么說,Caffeine緩存
2、在GuavaCache中,並不存在任何線程!它實現機制是在寫操作時順帶做少量的維護工作(如清除),偶爾在讀操作時做(如果寫操作實在太少的話),也就是說在使用的是調用線程
參考:GuavaCache簡介(一)是輕量級的框架 少量數據,並且 過期時間相同 可以用 GuavaCache
十一、設計模式,生產者消費者設計模式,以及wait,notifiy的運用,阿里問了一個面試、終極目的是用wait和notify實現一個可重入鎖來保證順序;
問題:使用“生產者-消費者模式”編寫代碼實現:線程A隨機間隔(10~200ms)按順序生成1到100的數字(共100個),
放到某個隊列中.3個線程B、C、D即時消費這些數據,線程B打印(向控制台)所有被2整除的數,
線程C打印被3整除的數,線程D打印其它數據,要求數字的打印是有序的(從1到100)
限時40分鍾,可以使用IDE及第三方類庫
這個類似的問題美團也問過,不過美團直接說能否用synchronized 實現一個可重入鎖,本質上也是使用了wait和notify()函數
參考:使用synchronized 實現ReentrantLock(美團面試題目)
十二、加解密算法 每次向對方的服務器發送數據,都需要傳遞token 這是什么機制?
這是非對稱加密 參考:聊聊對稱/非對稱加密在HTTPS中的使用
最后一百、架構設計知識
1、是否了解DDD 架構設計
分為四層,(1)基礎設施層(2)領域服務層(3)應用服務層(4)用戶接口層
用戶界面層:負責向用戶展示信息或解釋用戶指令,如:天網的前端界面,調用天網服務的外賣應用程序等。
應用層:定義軟件要完成的任務,指揮領域層的對象或調用其他應用服務來解決問題 ,不包含業務規則和知識,要求盡量的簡單。
領域層:負責表達業務概念,業務狀態信息以及業務規則。 核心
基礎設施層:為上面各層提供通用的技術能力:比如 以上各層交互的Thrfit,為領域層提供持久化機制的zebra,公司為UI層提供的通用組件 等等。
以天網量化分級導入功能為例:
用戶界面層:天網前端界面
應用層:負責參數校驗、excel解析、調用mdc接口驗證門店是否存在、調用領域層進行檢查 和存儲。
領域層:量化分級模塊 領域對象(Entity、Value Objext)、以及 操作服務(Service)。
基礎設施層:Thrift、MCC、zebra等。
參考:DDD(Domain Driven Design) 架構設計
參考:阿里P6-P7面試准備