polin被吊打
MatrixYG是宇宙條的工程師,手撕紅黑樹,LeetCode800題,國内ICPC牌子拿到手軟,腳踩八股文,阿裡位元組資深劃水工程師。這天他聽說好朋友polin面試被吊打了:

MatrixYG帶着自己的位元組工牌(pdd買的),來到了這家公司。
面試開始
MatrixYG來到了公司的面試房間,迎面走來的是一個看着不是很年輕的大哥,看上去有點東西。當場有點虛:
但是既來之則安之,MatrixYG很快鎮定下來。簡單自我介紹:
- MatrixYG:面試官你好,我是xx。現就職于xx,職位是後端開發工程師。主要負責我們部門xx。在工作的兩年多裡面主要做了xx。我的大學是畢業于xx,。。。。
- 面試官:掃了一眼MatrixYG脖子上pdd買來的工牌說到:好的,看你履歷很優秀啊。我看你履歷上寫的主要是Java技術棧,那我們聊點Java基礎?
- MatrixYG:完全OK。(内心OS:八股文我昨天才背過,嘿嘿)
MatrixYG帶着位元組工牌去面試,沒想到面試官問了這些!
第一回合
面試官:
能給我講講Java為什麼能夠跨平台嗎?
MatrixYG内心一激靈,知道了面試官的套路了。這哥們上來就問我這個問題,看來是要問我虛拟機啊。懂了,看我的。
MatrixYG:
Java之是以能夠跨平台,是依賴于Java虛拟機,Java程式被編譯成了位元組碼檔案,而不是直接能夠在計算機上運作的二進制檔案。Java代碼真正執行的時候,是通過虛拟機執行的,不同平台适配不同的虛拟機。是以Java虛拟機在這裡就是一個翻譯官的角色,有了它Java位元組碼就能在不同的平台上運作。
面試官内心os:小子,入套了吧。你不說虛拟機我還不好意思問你呢,這可是你自己找上門的。
嗯,了解的不錯。看你提到了Java虛拟機,能給我講講虛拟機的記憶體區域一般都包括哪些嗎?
MatrixYG:嘿嘿,這題我會!趕緊說道:
好的。其實虛拟機的記憶體區域按照不同的視角,可以有不同的劃分。如果按照線程的視角,可以分為兩種(不經意間提到線程,就等你問我)。線程私有和線程共享區。
(1)程式計數器PC。這個代表了目前位元組碼執行到了哪個位置
(2)虛拟機棧
(3)本地方法棧
這三個是線程私有,還有兩個是線程共享
(4)方法區
(5)虛拟機堆
這兩個區域是所有線程都共享的。
但是如果是GC(又不經意間透露出我懂這個)的角度,可以劃分為新生代,老年代,永久代。不同的區域GC政策可以采取不一樣的GC政策,來達到最優的GC性能。
面試官:這小子看來八股背的不少啊,有bear而來!
那我就繼續了。繼續問到:
“嗯,不錯。你剛才提到了虛拟機棧和本地方法棧,這是倆是幹啥的。能講講差別嗎?”
MatrixYG:這面試官咋不按套路出牌,難道接下來不是問我線程或者GC嗎???
但是,還是微笑回答道:
好的,我們知道調用函數的時候是需要儲存函數相關的資訊的,比如參數等。這個需要一個棧的結構來儲存調用現場。虛拟機棧就是給虛拟機内部的函數調用使用的棧結構,本地方法棧是調用本地方法使用的棧結構。 ”他們兩個的使用範圍不一樣說的廢話
面試官:小夥子,這個答案可不是我想要的哦。就追問道:
什麼叫做本地方法?
MatrixYG:内心一咯噔,突然忘了。
但是思考了1ms說到:
本地方法指的是作業系統提供的一些方法。因為虛拟機其實需要和作業系統互動,有些能力需要借助作業系統内部提供的api完成。調用這些方法的時候也需要棧結構儲存資訊。比如Object類的hashCode就依賴本地方法,一般依賴本地方法的都适用native關鍵字修飾。
面試官: 嗯,放你一馬。差點被我逮住把柄。說到:
好的,了解的不錯。剛才聽你說到了垃圾回收,能講講Java的垃圾回收機制嘛
MatrixYG:虛驚一場,幸虧平時經常看jdk的源碼。腦子差點沒轉過來。。什麼?問我GC?這次終于被我逮住了吧:
然後微笑的說道:
Java的垃圾回收主要分為兩個重要的階段,第一個階段判斷虛拟機内部的哪些類是垃圾,已經可以被回收。第二步是執行回收的操作。
Matrix心想,暫時說這麼多。後面再展開。
面試官:嗯,看來這小子等着我問他啊。換個方向,看看他是不是在背八股文。
說到:
”是的,看你條理比較清晰。估計是對GC有一定了解,能給我講講GC現在一些進展嗎“
嘿嘿,考察一下你的技術視野,如果隻懂得什麼複制,清除,之類的,那可别怪我開門左轉了。
MatrixYG:嗯?怎麼不問我回收算法。垃圾回收器?不問我新生代,老年代這些??看來這個面試官确實有點東西啊。不按套路出牌。幸虧我前幾天關注了ZZTPeople,上面講了一下。
好的,其實我對GC還是非常感興趣的。最近釋出的JDK15把ZGC成為正式支援的GC回收器,其實在此之前還有CMS和G1,這兩種回收器性能也是很好的。以G1為例子,G1垃圾回收器性能瓶頸主要在于
(1)初始标記階段:初始标記階段是指從GC Roots出發标記全部直接子節點的過程。
(2)再标記階段:重新标記那些在并發标記階段發生變化的對象。
(3)清理階段清點出有存活對象的分區和沒有存活對象的分區,該階段不會清理垃圾對象,也不會執行存活對象的複制。
(4)轉移階段。轉移階段需要配置設定新記憶體和複制對象的成員變量。轉移階段是STW的,其中記憶體配置設定通常耗時非常短,但對象成員變量的複制耗時有可能較長,這是因為複制耗時與存活對象數量與對象複雜度成正比。對象越複雜,複制耗時越長。是以G1的性能瓶頸在轉移階段。ZGC解決了這個問題,在轉移階段也是并發的,并且ZGC之是以是常數級别的耗時,不随着JVM堆增大是因為他隻和目前GCROOT數量有關,這個是常數級别的
面試官:哦呦,這小夥子看來對GC還是有點了解的啊。可以比上次那個polin啥隻會八股要強一點了。
說到:
嗯,可以。ZGC裡面為了實作這些東西都有哪些方案,能介紹一下嗎
MatrixYG: 嘿嘿,這次我還會!就不慌不忙的解釋道:
== G1之是以在轉移階段不适用并發,是因為對于對象不好判斷是否被回收了。假設GC線程轉移了一個對象,但是沒有來及更新對象位址應用程式還能繼續通路到。這就出現了不一緻。ZGC通過讀屏障和着色指針來保證并發轉移的安全==
面試官:
說到:
“很不錯,那你介紹一下這兩種技術吧”
MatrixYG: 這有點頂啊,都快到知識盲區了。
微笑說道:
讀屏障其實是一種插樁技術。JVM會向代碼中插入一小段代碼,當應用程式使用對象時,就會執行這段代碼。如果對象位址變了,那麼讀屏障就會把位址更新。着色指針實際上是一種使用指針記錄資訊的操作
對于64位的作業系統,指針式一個字(word)64位。這64位可以被劃分為三部分
(1)第一部分:18位。暫時不用
(2)第二部分:4位。分别表示對象資訊。
(4)最後42位表示對象位址。
類似這種。當對象被修改之後,就會更新中間四位資訊。每次gc直接掃描這個着色指針即可。
面試官:哦呦,看來這小夥子确實不錯。那GC就到這裡了。他還提到了插樁技術,莫非對動态位元組碼有所研究?這個我自己也不太會,就不問了。
說到:
嗯,确實不錯。我們聊聊下個話題。
第二回合
面試官:
看來你對虛拟機有一定的了解,剛才聽你說了線程。能介紹一些Java有哪些建立線程的方式嘛。
MatrixYG:
在Java程式裡一般有三種方式建立線程,分别是:
(1)繼承Thread類
(2)實作Runnable接口
(3)實作Callable接口
其中1和2沒有太大的差別,主要是一個是繼承類,一個是實作接口。2和3的主要差別是Callable有傳回值,但是runnable的run方法沒有傳回值
class Task implements Callable<Integer>{
@Override
public Integer call() throws Exception {
return null;
}
}
class Task2 implements Runnable{
@Override
public void run() {
}
}
面試官:
好的,你提到了callable。我們使用callable經常回個futrue配合起來使用,能講講這個嗎
MatrixYG:好的。
future是一種回調機制,比如我們使用callable建立一個任務的時候,不知道這個任務的狀态。future提供了這種機制,可以讓我們知道任務是否完成,或者取消任務。
例如:
public static void main(String[] args) throws Exception {
FutureTask<Integer> futureTask = new FutureTask<>(new Task());
futureTask.get();
}
get方法會得到結果。但是這是一個阻塞方法,是以一般我們會制定timeout時間。
面試官:
嗯,小夥子還可以。那看來你對線程同步也很有了解吧。我就不浪費時間了,我們來寫個題吧
MatrixYG:大喜。看來面試官人很好啊。終于到了寫題環節了。
第三回合
面試官:
數字 n 代表生成括号的對數,請你設計一個函數,用于能夠生成所有可能的并且 有效的 括号組合。
例子:
輸入:n = 1
輸出:["()"]
MatrixYG假裝思考了30s之後說到,這題我會了。這題可以采用深度優先搜尋,考慮整個解空間是一個二叉樹,往左走達标左括号,往右走代表右括号。那麼搜尋n層,每一條合法的路徑就是答案。比如現在n=2,那麼就表示左右括号分别2個。搜尋的标準就是看目前還有幾個括号,然後嘗試加個左括号,嘗試加個右括号。對于任意一個中間序列來說,左括号的數量一定是要比右括号少的。如果不符合就是非法序列。最後搜到底,就得到了全部答案。
面試官:哦呦,這小夥子可以啊。四位也挺靈活的。說的正解。就問道:
那你這個算法的時空複雜度是多少呢?
MatrixYG:算法的時間複雜度和空間複雜度都是O(2^n)的。因為搜尋每次都有兩種選擇,是以解空間的大小和數量都是固定的,但是可以加上一些不合法的剪枝加速,不過這并不影響時間複雜度。
面試官:可以小夥子,你過了。
好的,那你實作一下
MatrixYG:
class Solution {
public:
vector<string>ans;
void dfs(string curAns,int l,int r){
if(l==0&&r==0){
ans.push_back(curAns);
return ;
}
if(l>r){
return;
}
if(l>0){
dfs(curAns+"(",l-1,r);
}
if(r>0){
dfs(curAns+")",l,r-1);
}
}
vector<string> generateParenthesis(int n) {
if(n==0)return ans;
dfs("",n,n);
return ans;
}
};
1分鐘完事。
面試官看了直呼内行,說到
好的,我這邊你沒問題了。等下一個面試吧!加油