做到我這樣,你也能拿到京東Offer

2019-09-04     程式設計師聖經
作者:紅鼻子熊 
來源:公眾號Hollis

這是一篇7000字的長文,作者是一名211小碩,拿到了京東的研發崗Offer,總結了自己參加京東校招並拿到offer的面試題及答案,還有學習方法,Hollis只做了簡單的格式修改。

對於一個校招生來說,能把JVM、網絡知識、JDK源碼等了解的這麼全面,他不進京東,誰進京東。做到他這樣,你,也可以。

一面

面試時間較長,回答速度也較快,所有問題都進行了完整的回答。形式為電話面試,都是基礎,難度一般,不要緊張,回答知識點即可。

內容主要包括jvm相關,網絡知識(TCP/IP,DNS),JDK源碼(HashMap, ArrayList, HashTable等)

JVM部分

這部分主要考的是知識點的串聯能力,面試官提出一個問題時,要把該問題相關的知識點都羅列出來(在說之前可以詢問面試官是否需要詳細講述該知識點)。

參考書籍:深入理解Java虛擬機-周志明 神書!神書!神書!建議多刷幾遍,書中的所有知識點可以通過JAVA運行時區域和JAVA的內存模型與線程兩個大模塊羅列完全。

常考內容有:GC,JAVA線程實現方式,volatile底層原理,線程安全,鎖與CAS等

1. 講下JAVA的運行時區域

回答:運行時數據區整體分為兩類 線程私有和線程共享。

線程私有的包括:

  • 程序計數器
  • 若正在執行的是java方法,則計數器記錄的是正在執行的位元組碼指令的地址
  • 若正在執行的是native方法,則計數器為空
  • 該區域是唯一一個不會導致outofmemoryError的區域
  • 虛擬機棧
  • 描述的是Java方法執行的內存模型:每個方法都會創建一個棧幀用於存儲局部變量表,操作數棧,動態連結,方法出口等信息
  • 局部變量表存放了編譯期可知的基本數據類型,對象引用,和returnAddress類型(指向一條位元組碼指令地址),局部變量表的內存空間在編譯器確定,在運行期不變
  • 可導致兩種異常:線程請求的棧深度大於虛擬機允許的深度-StackOverflowError;虛擬機無法申請到足夠的內存-OutOfMemoryError
  • 本地方法棧
  • 和虛擬機棧類似,但它是為Native方法服務的

線程共享的包括:

  • java堆是被所有線程共享的內存區域,在虛擬機啟動時創建,用來分配對象實例和數組
  • 堆是垃圾回收器主要管理的區域,堆可分為新生代和老年代
  • 從內存分配角度看,堆可劃分出多個線程私有的分配緩衝區(TLAB)
  • 大小可通過 -Xmx 和 -Xms 控制
  • 方法區
  • 用來存放虛擬機加載的類信息,常量,靜態變量,即時編譯器編譯後的代碼等信息
  • GC會回收該區域的常量池和進行類型的卸載 *運行時常量池
  • Class文件的常量池用於存放編譯期生成的各種字面量和符號引用,這部分內容將在類加載後存放在運行時常量池中
  • 還把翻譯出來的直接引用也放在運行時常量池中,運行時產生的常量也放在裡面

2. 簡單說下垃圾回收機制

大致思路: 要進行垃圾回收,首先要判斷一個對象是否活著,這就引出了兩種方法…

引用計數法和可達性分析法
gc roots 類型
引用類型
兩次標記過程
垃圾回收算法
內存分配策略
觸發垃圾回收
垃圾回收器
也會回收方法區

回答:要進行垃圾回收,首先要判斷對象是否存活,引出了兩個方法:

  • 引用計數法
  • 思想:給對象設置引用計數器,沒引用該對象一次,計數器就+1,引用失效時,計數器就-1,當任意時候引用計數器的值都為0時,則該對象可被回收
  • Java不適用原因:無法解決對象互相循環引用的問題

  • 可達性分析法
  • 虛擬機棧(棧幀中的局部變量表)中引用的對象
  • 方法區中類靜態屬性引用的對象
  • 方法區中常量引用的對象
  • 本地方法棧中JNI(Native方法)引用的對象
  • 以GC Roots為起點,從這些起點開始向下搜索,經過的路徑稱為引用鏈。若一個對象到GC Roots之間沒有任何引用鏈,則該對象是不可達的。
  • 那麼可作為GC Roots的對象有

  • 在可達性分析過程中,對象引用類型會對對象的生命周期產生影響,JAVA中有這幾種類型的引用:
  • 強引用:只要該引用還有效,GC就不會回收
  • 軟引用:內存空間足夠時不進行回收,在內存溢出發生前進行回收、用SoftReference類實現
  • 弱引用:弱引用關聯的對象只能存活到下一次Gc收集、用WeakReference類實現
  • 虛引用:無法通過虛引用獲得對象實例,也不會對對象的生存時間產生影響、唯一目的:當該對象被Gc收集時,收到一個系統通知。用PhantomReference類實現

一個對象真正不可用,要經歷兩次標記過程:

  • 首先進行可達性分析,篩選出與GC Roots沒用引用鏈的對象,進行第一次標記
  • 第一次標記後,再進行一次篩選,篩選條件是是否有必要執行finalize()方法。若對象有沒有重寫finalize()方法,或者finalize()是否已被jvm調用過,則沒必要執行,GC會回收該對象
  • 若有必要執行,則該對象會被放入F-Queue中,由jvm開啟一個低優先級的線程去執行它(但不一定等待finalize執行完畢)。
  • Finalize()是對象最後一次自救的機會,若對象在finalize()中重新加入到引用鏈中,則它會被移出要回收的對象的集合。其他對象則會被第二次標記,進行回收

JAVA中的垃圾回收算法有:

  • 標記-清除(Mark-Sweep)
  • 兩個階段:標記, 清除
  • 缺點:兩個階段的效率都不高;容易產生大量的內存碎片

  • 複製(Copying)
  • 把內存分成大小相同的兩塊,當一塊的內存用完了,就把可用對象複製到另一塊上,將使用過的一塊一次性清理掉
  • 缺點:浪費了一半內存

  • 標記-整理(Mark-Compact)
  • 標記後,讓所有存活的對象移到一端,然後直接清理掉端邊界以外的內存

  • 分代收集
  • 把堆分為新生代和老年代
  • 新生代使用複製算法
  • 將新生代內存分為一塊大的Eden區和兩塊小的Survivor;每次使用Eden和一個Survivor,回收時將Eden和Survivor存活的對象複製到另一個Survivor(HotSpot的比例Eden:Survivor = 8:1)
  • 老年代使用標記-清理或者標記-整理

觸發GC又涉及到了內存分配規則: (對象主要分配在Eden,若啟動了本地線程分配緩衝,將優先在TLAB上分配)

  • 對象優先在Eden分配
  • 當Eden區沒有足夠的空間時就會發起一次Minor GC
  • 大對象直接進入老年代
  • 典型的大對象是很長的字符串和數組
  • 長期存活的對象進入老年代
  • 每個對象有年齡計數器,每經過一次GC,計數器值加一,當到達一定程度時(默認15),就會進入老年代
  • 年齡的閾值可通過參數 -XX:MaxTenuringThreshold設置
  • 對象年齡的判定
  • Survivor空間中相同年齡所有對象大小的總和大於Survivor空間的一半,年齡大於等於該年齡的對象就可直接進入老年代,無須等到MaxTenuringThreshold要求的年齡
  • 空間分配擔保
  • 發生Minor GC前,jvm會檢查老年代最大可用的連續空間是否大於新生代所有對象總空間,若大於,則Minor GC是安全的
  • 若不大於,jvm會查看HandlePromotionFailure是否允許擔保失敗,若不允許,則改為一次Full GC
  • 若允許擔保失敗,則檢查老年代最大可用的連續空間是否大於歷次晉升到老年代對象的平均大小,若大於,則嘗試進行Minor GC;若小於,則要改為Full GC

垃圾收集器:

  • Serial(串行收集器)
  • 特性:單線程,stop the world,採用複製算法
  • 應用場景:jvm在Client模式下默認的新生代收集器
  • 優點:簡單高效

  • ParNew
  • 特點:是Serial的多線程版本,採用複製算法
  • 應用場景:在Server模式下常用的新生代收集器,可與CMS配合工作

  • Parallel Scavenge
  • 特點:並行的多線程收集器,採用複製算法,吞吐量優先,有自適應調節策略
  • 應用場景:需要吞吐量大的時候

  • SerialOld
  • 特點:Serial的老年代版本,單線程,使用標記-整理算法

  • Parallel Old
  • Parallel Scavenge的老年代版本,多線程,標記-整理算法

  • CMS
  • 對CPU資源敏感
  • 無法處理浮動垃圾(並發清除 時,用戶線程仍在運行,此時產生的垃圾為浮動垃圾)
  • 產生大量的空間碎片
  • 初始標記:stop the world 標記GC Roots能直接關聯到的對象
  • 並發標記:進行GC Roots Tracing
  • 重新標記:stop the world;修正並發標記期間因用戶程序繼續運作而導致標記產生變動的 那一部分對象的標記記錄
  • 並發清除:清除對象
  • 特點:以最短回收停頓時間為目標,使用標記-清除算法
  • 過程:
  • 優點:並發收集,低停頓
  • 缺點:

  • G1
  • 初始標記:stop the world 標記GC Roots能直接關聯到的對象
  • 並發標記:可達性分析
  • 最終標記:修正在並發標記期間因用戶程序繼續運作而導致標記產生變動的那一部分標記記錄
  • 篩選回收:篩選回收階段首先對各個Region的回收價值和成本進行排序,根據用戶所期望的GC停頓時間來制定回收計劃
  • 並行與並發
  • 分代收集
  • 空間整合:從整體看是基於「標記-整理」的,從局部(兩個region之間)看是基於「複製」的。
  • 可預測的停頓:使用者可明確指定在一個長度為M毫秒的時間片段內,消耗在垃圾收集上的時間不得超過N毫秒。
  • 特點:面向服務端應用,將整個堆劃分為大小相同的region。
  • 執行過程:
  • GC自適應調節策略 Parallel Scavenge收集器有一個參數-XX:+UseAdaptiveSizePolicy。當這個參數打開之後,就不需要手工指定新生代的大小、Eden與Survivor區的比例、晉升老年代對象年齡等細節參數了,虛擬機會根據當前系統的運行情況收集性能監控信息,動態調整這些參數以提供最合適的停頓時間或者最大的吞吐量,這種調節方式稱為GC自適應的調節策略(GC Ergonomics)。

(垃圾回收器部分重點講CMS和G1)

最後提一下也會回收方法區:

  • 永久代中主要回收兩部分內容:廢棄常量和無用的類
  • 廢棄常量回收和對象的回收類似
  • 無用的類需滿足3個條件
  • 該類的所有實例對象已被回收
  • 加載該類的ClassLoader已被回收
  • 該類的Class對象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法

上面的知識點在你多刷幾遍書,腦中形成相應的知識網後能很全面的說出來。

網絡部分

網絡知識在面試中非常重要,尤其是TCP,DNS,HTTP等知識點。

該部分我的參考書籍是:圖解HTTP,圖解TCP/IP(對於開發來說,這兩本書在網絡方面的講解應該夠用了),以及相關博客。

回答該類問題時,依然要從面試官提到的問題進行擴散,把相關的問題自己拋出來進行講述(在拋出講述時可以詢問面試官是否需要進行詳細的講解)

1. 講一下TCP三次握手

對於該問題,可以直接關聯TCP四次分手進行回答。若是當場面試,可以在紙上畫出客戶端和服務端的TCP狀態序列



然後自己可以拋出相關的問題進行回答,如:

為什麼不採用兩次握手,SYN半連接攻擊,TIMEWAIT數量太多怎麼辦,為什麼連接的時候是3次握手,關閉的時候是4次分手,為什麼TIMEWAIT狀態需要經過2MSL(最大報文段生存時間)才能回到CLOSE狀態等等問題(這些問題在網上都有講解,這裡就不贅述了)。

2. TCP和UDP的區別(很常見的問題):

  • TCP面向連接(如打電話要先撥號建立連接) UDP是無連接的,即發送數據之前不需要建立連接
  • TCP提供可靠的服務。也就是說,通過TCP連接傳送的數據,無差錯,不丟失,不重複,且按序到達;UDP盡最大努力交付,即不保證可靠交付
  • TCP面向位元組流,實際上是TCP把數據看成一連串無結構的位元組流;UDP是面向報文的
  • UDP沒有擁塞控制,因此網絡出現擁塞不會使源主機的發送速率降低(對實時應用很有用,如IP電話,實時視頻會議等)
  • 每一條TCP連接只能是點到點的;UDP支持一對一,一對多,多對一和多對多的交互通信
  • TCP首部開銷20位元組;UDP的首部開銷小,只有8個位元組
  • TCP的邏輯通信信道是全雙工的可靠信道,UDP則是不可靠信道

3. 講下ARP原理

  • 根據目標Ip地址藉助ARP請求和ARP響應來確定目標的MAC地址
  • 原理:通過廣播發送ARP請求,Ip地址一致的主機接收該請求,然後將自己的MAC地址加入的ARP響應包返回給源主機。要進行MAC地址緩存來避免占用網絡流量
  • 每個主機都會在自己的ARP緩衝區建立一個ARP列表,表示Ip地址和MAC地址的對應關係
  • 當源主機要發送數據時,首先檢查ARP列表中是否有對應Ip地址的目標主機的MAC地址,若有,則直接發送,若無,則向本網段的所有主機發送ARP數據包,該數據包包括的內容有:源Ip地址,源MAC地址,目標主機Ip地址
  • 當本網絡的所有主機收到該ARP數據包時,首先檢查書包中的Ip地址是否是自己的Ip地址,若不是則丟棄,若是,則首先從數據包中取出源主機的IP和MAC地址寫入自己的ARP列表中,若已存在,則覆蓋;然後將自己的MAC地址寫入ARP響應包中,告訴源主機自己的MAC地址
  • 源主機收到ARP響應包後,將目標主機的IP和MAC地址寫入ARP列表中。若源主機一直沒有收到ARP響應,則ARP查詢失敗
  • (廣播發送ARP請求,單播發送ARP響應) 此時,自己可以加上ARP攻擊及免費ARP相關知識點(可自行搜索)

JDK源碼部分

這部分主要在於自己平時的積累,可以跟著相關的博客看源碼。常考的內容有String,集合框架,foreach(Iterator及fail-fast機制)等內容。

1. HashMap了解嗎,說一下

這裡就不詳細說了,大致思路是jdk7和jdk8的實現原理及區別(重點有實現的數據結構,存儲單元從Entry到Node的轉變,加載因子,什麼時候擴容,jdk1.8擴容的具體實現方式等等),HashMap和HashTable的區別,HahsMap和HashSet的關係。要結合源碼說。

ArrayList了解嗎,說一下

重點是底層實現方式,擴容機制,以及LinkedList的底層實現方式;它們之間的區別。要結合源碼說

二面

面試時間不長,個別問題回答的有些底虛。主要考察的是知識面的廣度和對技術的熱愛程度,以及對做過項目的熟悉程度。還是那句話,坦誠地回答問題,不會就是不會,如果假裝會但是被接下來地問題問倒的話,那就沒戲了。

實習時項目的相關問題

每個人的項目都不同,只列舉幾個問題

項目是怎樣預防sql注入的

回答:用的是mybatis,sql語句中用#{},#{}表示一個占位符號,通過#{}可以實現preparedStatement向占位符中設置值,jdbc有個預編譯的過程可以有效預防sql注入,儘量不用${},它是個拼接符,用來拼接sql字符串。

項目中你做到的redis緩存相關的切面配置(簡歷中有寫)

  • 切面分析:@Aspect
  • 切面: 查詢前先查詢redis,若查詢不到,則查資料庫,得到數據後存到redis中
  • 目標方法:查詢資料庫
  • 前置:查詢之前先查redis
  • 後置:從資料庫中查到的內容放到redis中
  • 切面中的通知定位環繞通知:@Around

然後又問了aop的實現原理(jdk動態代理和cglib位元組碼增強,在回答時要說出底層源碼)

喜歡什麼技術

答:分布式,實習時雖然自己做的是企業後台,並沒有涉及到很多分布式的內容,但是會經常從同事那裡了解一些分布式的技術感覺很有趣

都了解到了哪些分布式的技術

答:進行反向代理和負載均衡的nginx,及實現高可用的keepalived+nginx;內存資料庫redis及它的基本數據類型和持久化方式;用於做註冊中心的zookeeper和服務治理的dubbo;防止用戶重複登錄的單點登錄;分布式的文件存儲系統fastdfs;頁面靜態化處理的freemarker;以及用於搜索的solr(這部分由於有些技術自己只是會用,不了解底層,所以說的吞吞吐吐,很沒底氣,其實沒必要,大大方方說出來後再說明自己了解的程度即可)

感覺自己哪方面有欠缺

答:spring源碼不太了解,另外linux方面有些薄弱,正在補充相關知識。(可以加上自己目前正在看哪些書)

jdk9有哪些認識

答:不太清楚,還沒了解(內心狀態:臥槽,jdk9什麼時候出來的) (這個問題可以很好的了解到求職者對技術的熱愛程度,平時可以多關注技術的發展方向,版本疊代。這方面可以通過關注一些優質的公眾號:如Hollis)

為什麼想來網際網路公司

答:可能是第一份實習工作的影響吧, 面試官:還有呢? 我:我也說不清楚,就是想去網際網路公司(說完之後就想宰了自己)

HR面

hr面重要的也是真誠,還有就是表達好自己想去該公司及對該公司的了解

目前有幾個offer

答:1個,但是已經拒了

為什麼拒了

答:因為它不是網際網路公司

你為什麼想在網際網路公司發展

答:工資高是一方面,另外在網際網路公司中一般可以更快接觸到新的技術

為什麼想來京東

答:因為京東是我關注很久的公司,從京東第一次做秋招視頻直播就開始關注了。首先京東是一家網際網路公司,其次京東近些年的發展有目共睹,而且京東正在向技術公司轉型,相信自己可以學到很多東西。

你有什麼缺點

答:壓力大時喜歡通過吃東西來排解壓力,所以又要減肥什麼的很麻煩

還有呢

答:喜歡熬夜

還有呢

答:emmmm,哎呀,想不起來了 (其實,關於優點和缺點的問題應該好好總結一下,不然問的當時容易懵逼) 還有幾個問題不太記得了,大概就是實習時自己意見和同事不一樣時是怎麼解決的。對於這種問題,最好舉個實際的例子。

總結

其實網上的大多數面經已經說的很清楚,一定要形成自己的知識樹,如果僅僅想通過看面經來通過面試,會很難。自己平常一定要積累知識,把知識點分類進行記錄,在面試過程中主動說出面試官所提問題的關聯問題的解決方案能夠加分,體現自己的知識串聯能力。

文章來源: https://twgreatdaily.com/zh/j3Dt_GwBJleJMoPMcCEu.html