天天看點

MatrixYG帶着位元組工牌去面試,沒想到面試官問了這些!

polin被吊打

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

MatrixYG帶着位元組工牌去面試,沒想到面試官問了這些!

MatrixYG帶着自己的位元組工牌(pdd買的),來到了這家公司。

MatrixYG帶着位元組工牌去面試,沒想到面試官問了這些!

面試開始

MatrixYG來到了公司的面試房間,迎面走來的是一個看着不是很年輕的大哥,看上去有點東西。當場有點虛:

MatrixYG帶着位元組工牌去面試,沒想到面試官問了這些!
MatrixYG帶着位元組工牌去面試,沒想到面試官問了這些!

但是既來之則安之,MatrixYG很快鎮定下來。簡單自我介紹:

  • MatrixYG:面試官你好,我是xx。現就職于xx,職位是後端開發工程師。主要負責我們部門xx。在工作的兩年多裡面主要做了xx。我的大學是畢業于xx,。。。。
  • 面試官:掃了一眼MatrixYG脖子上pdd買來的工牌說到:好的,看你履歷很優秀啊。我看你履歷上寫的主要是Java技術棧,那我們聊點Java基礎?
  • MatrixYG:完全OK。(内心OS:八股文我昨天才背過,嘿嘿)
    MatrixYG帶着位元組工牌去面試,沒想到面試官問了這些!

第一回合

面試官:

能給我講講Java為什麼能夠跨平台嗎?

MatrixYG内心一激靈,知道了面試官的套路了。這哥們上來就問我這個問題,看來是要問我虛拟機啊。懂了,看我的。

MatrixYG帶着位元組工牌去面試,沒想到面試官問了這些!

MatrixYG:

Java之是以能夠跨平台,是依賴于Java虛拟機,Java程式被編譯成了位元組碼檔案,而不是直接能夠在計算機上運作的二進制檔案。Java代碼真正執行的時候,是通過虛拟機執行的,不同平台适配不同的虛拟機。是以Java虛拟機在這裡就是一個翻譯官的角色,有了它Java位元組碼就能在不同的平台上運作。

面試官内心os:小子,入套了吧。你不說虛拟機我還不好意思問你呢,這可是你自己找上門的。

嗯,了解的不錯。看你提到了Java虛拟機,能給我講講虛拟機的記憶體區域一般都包括哪些嗎?

MatrixYG:嘿嘿,這題我會!趕緊說道:

好的。其實虛拟機的記憶體區域按照不同的視角,可以有不同的劃分。如果按照線程的視角,可以分為兩種(不經意間提到線程,就等你問我)。線程私有和線程共享區。

(1)程式計數器PC。這個代表了目前位元組碼執行到了哪個位置

(2)虛拟機棧

(3)本地方法棧

這三個是線程私有,還有兩個是線程共享

(4)方法區

(5)虛拟機堆

這兩個區域是所有線程都共享的。

但是如果是GC(又不經意間透露出我懂這個)的角度,可以劃分為新生代,老年代,永久代。不同的區域GC政策可以采取不一樣的GC政策,來達到最優的GC性能。

面試官:這小子看來八股背的不少啊,有bear而來!

MatrixYG帶着位元組工牌去面試,沒想到面試官問了這些!

那我就繼續了。繼續問到:

“嗯,不錯。你剛才提到了虛拟機棧和本地方法棧,這是倆是幹啥的。能講講差別嗎?”

MatrixYG:這面試官咋不按套路出牌,難道接下來不是問我線程或者GC嗎???

MatrixYG帶着位元組工牌去面試,沒想到面試官問了這些!

但是,還是微笑回答道:

好的,我們知道調用函數的時候是需要儲存函數相關的資訊的,比如參數等。這個需要一個棧的結構來儲存調用現場。虛拟機棧就是給虛拟機内部的函數調用使用的棧結構,本地方法棧是調用本地方法使用的棧結構。 ”他們兩個的使用範圍不一樣說的廢話

面試官:小夥子,這個答案可不是我想要的哦。就追問道:

什麼叫做本地方法?

MatrixYG:内心一咯噔,突然忘了。

MatrixYG帶着位元組工牌去面試,沒想到面試官問了這些!

但是思考了1ms說到:

本地方法指的是作業系統提供的一些方法。因為虛拟機其實需要和作業系統互動,有些能力需要借助作業系統内部提供的api完成。調用這些方法的時候也需要棧結構儲存資訊。比如Object類的hashCode就依賴本地方法,一般依賴本地方法的都适用native關鍵字修飾。

MatrixYG帶着位元組工牌去面試,沒想到面試官問了這些!

面試官: 嗯,放你一馬。差點被我逮住把柄。說到:

好的,了解的不錯。剛才聽你說到了垃圾回收,能講講Java的垃圾回收機制嘛

MatrixYG:虛驚一場,幸虧平時經常看jdk的源碼。腦子差點沒轉過來。。什麼?問我GC?這次終于被我逮住了吧:

MatrixYG帶着位元組工牌去面試,沒想到面試官問了這些!

然後微笑的說道:

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帶着位元組工牌去面試,沒想到面試官問了這些!
MatrixYG帶着位元組工牌去面試,沒想到面試官問了這些!
MatrixYG帶着位元組工牌去面試,沒想到面試官問了這些!

說到:

“很不錯,那你介紹一下這兩種技術吧”

MatrixYG: 這有點頂啊,都快到知識盲區了。

MatrixYG帶着位元組工牌去面試,沒想到面試官問了這些!
MatrixYG帶着位元組工牌去面試,沒想到面試官問了這些!
MatrixYG帶着位元組工牌去面試,沒想到面試官問了這些!

微笑說道:

讀屏障其實是一種插樁技術。JVM會向代碼中插入一小段代碼,當應用程式使用對象時,就會執行這段代碼。如果對象位址變了,那麼讀屏障就會把位址更新。着色指針實際上是一種使用指針記錄資訊的操作

對于64位的作業系統,指針式一個字(word)64位。這64位可以被劃分為三部分

(1)第一部分:18位。暫時不用

(2)第二部分:4位。分别表示對象資訊。

(4)最後42位表示對象位址。

MatrixYG帶着位元組工牌去面試,沒想到面試官問了這些!

類似這種。當對象被修改之後,就會更新中間四位資訊。每次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分鐘完事。

MatrixYG帶着位元組工牌去面試,沒想到面試官問了這些!

面試官看了直呼内行,說到

好的,我這邊你沒問題了。等下一個面試吧!加油

繼續閱讀