1. 程式人生 > >面試題整理2018

面試題整理2018

要跳槽了,整理一份面試,方便檢視。(不斷更新中。。。。)

JVM:

什麼是JVM

JVM即java 虛擬機器 ,是Java程式執行的平臺,它就像一臺虛擬出來的計算機一樣,負責執行Java編譯好的位元組碼檔案

JVM的記憶體結構包括五大區域:程式計數器、虛擬機器棧、本地方法棧、堆區、方法區。其中程式計數器、虛擬機器棧、本地方法棧3個區域隨執行緒而生、隨執行緒而滅,因此這幾個區域的記憶體分配和回收都具備確定性,就不需要過多考慮回收的問題,因為方法結束或者執行緒結束時,記憶體自然就跟隨著回收了。而Java堆區和方法區則不一樣,這部分記憶體的分配和回收是動態的,正是垃圾收集器所需關注的部分。

  垃圾收集器在對堆區和方法區進行回收前,首先要確定這些區域的物件哪些可以被回收,哪些暫時還不能回收,這就要用到判斷物件是否存活的演算法!

1、JVM 的記憶體劃分

1. 程式計數器 (Program Counter Register)

概念:一塊較小的記憶體空間,是位元組碼直譯器的行為指示器 。

2. Java 虛擬機器棧 (VM Stack)

概念:Java 方法執行的記憶體模型,每一個java 方法執行,就會生成一個棧幀,

java 方法執行的過程就是棧幀在虛擬機器中入棧出棧的過程

3. 本地方法棧 (Native Method Stack)

概念:執行 Native 方法的棧

4. Java 堆 (Java Heap)

概念:所有執行緒共享的一塊記憶體區域,在虛擬機器啟動時建立。此記憶體區域唯一的目的就是存放物件例項,幾乎所有的物件都在這分配記憶體。

5. 方法區 (Method Area)

概念:儲存已被虛擬機器載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼資料等

2、垃圾回收機制

判斷何時執行 GC 的兩種演算法:

    1、引用計數演算法:

給物件新增一個引用計數器,每當有一個地方引用它時,計數器值加1;當引用失效時,計數器減1;任何時刻計數器都為0的物件就是不可能再被使用的。但是Java語言中沒有選用引用計數演算法來管理記憶體,其中最主要的一個原因是它很難解決物件之間相互迴圈引用的問題。

優點:引用計數收集器可以很快的執行,交織在程式執行中。對程式需要不被長時間打斷的實時環境比較有利。

缺點:無法檢測出迴圈引用。如父物件有一個對子物件的引用,子物件反過來引用父物件。他們的引用計數永遠不可能為0。

    2、可達性分析演算法

通過一系列名為"GC Roots"的物件作為起始點,從這些節點開始向下搜尋,搜尋所走過的路徑稱為引用鏈(Reference Chain),當一個物件到GC Roots沒有任何引用鏈相連時,則證明此物件是不可用的

 在Java語言裡,可作為GC Roots物件的包括如下幾種:               a.虛擬機器棧(棧楨中的本地變量表)中的引用的物件               b.方法區中的類靜態屬性引用的物件               c.方法區中的常量引用的物件               d.本地方法棧中JNI的引用的物件

幾種垃圾回收演算法:

    1、標記-清除演算法(Mark-Sweep)

從根節點開始標記所有可達物件,其餘沒有標記的即為垃圾物件,執行清除。但回收後的空間是不連續的

但由於標記-清除演算法直接回收不存活的物件,因此會造成記憶體碎片。

    2、標記-整理法:

     標記-整理演算法採用 標記-清除演算法一樣的方式進行物件的標記,但在清除時,在回收不存活的物件佔用的空間後,

會將所有的存活物件往左端空閒空間移動,並更新相應的指標。標記-整理演算法是在標記-清除演算法的基礎上,

又進行了物件的移動,因此成本更高,但是卻解決了記憶體碎片的問題。

    3、複製演算法:

    複製演算法採用從根集合掃描,並將存活物件複製到一塊新的,沒有使用過的空間中,這種演算法當控制元件存活的物件             比較少時,極為高效,但是帶來的成本是需要一塊記憶體交換空間進行物件的移動。

   4、分代收集演算法:

它的核心思想是根據物件存活的生命週期將記憶體劃分為若干個不同的區域。一般情況下將堆區劃分為老年代(Tenured Generation)和新生代(Young Generation),在堆區之外還有一個代就是永久代(Permanet Generation)

3、GC什麼時候觸發?

GC有兩種型別:Minor GC 和 Full GC()。

Minor GC

  一般情況下,當新物件生成,並且在Eden申請空間失敗時,就會觸發Minor GC,對Eden區域進行GC,清除非存活物件,並且把尚且存活的物件移動到Survivor區。然後整理Survivor的兩個區。這種方式的GC是對年輕代的Eden區進行,不會影響到年老代。因為大部分物件都是從Eden區開始的,同時Eden區不會分配的很大,所以Eden區的GC會頻繁進行。因而,一般在這裡需要使用速度快、效率高的演算法,使Eden去能儘快空閒出來。

Full GC

  對整個堆進行整理,包括Young、Tenured和Perm。Full GC因為需要對整個堆進行回收,所以比Minor GC要慢,因此應該儘可能減少Full GC的次數。在對JVM調優的過程中,很大一部分工作就是對於Full GC的調節。有如下原因可能導致Full GC:

a) 年老代(Tenured)被寫滿;

b) 持久代(Perm)被寫滿;

c) System.gc()被顯示呼叫;

d) 上一次GC之後Heap的各域分配策略動態變化;

4、類載入機制

一個Java檔案從編碼完成到最終執行,一般主要包括兩個過程

      編譯:把寫好的java檔案,通過javac命令編譯成位元組碼,也就是.class檔案。

      執行:把編譯生成的.class檔案交給Java虛擬機器(JVM)執行。

 類載入的過程:

      類載入過程即是指JVM虛擬機器把.class檔案中類資訊載入進記憶體,並進行解析生成對應的class物件的過程。

      類載入的過程主要分為三個部分: 載入、連線、初始化 ; 其中連線 又分為:驗證、準備、解析。

載入:

根據一個類的全限定名(如com.test.HelloWorld.class)來讀取此類的二進位制位元組流到JVM內部;

將位元組流所代表的靜態儲存結構轉換為方法區的執行時資料結構,轉換為一個與目標型別對應的java.lang.Class物件;

驗證:

主要是為了保證載入進來的位元組流符合虛擬機器規範,不會造成安全錯誤

驗證階段主要包括四個檢驗過程:檔案格式驗證、元資料驗證、位元組碼驗證和符號引用驗證;

準備:

為類中的所有靜態變數分配記憶體空間,併為其設定一個初始值

解析:

將常量池內的符號引用替換為直接引用的過程。這個階段可以在初始化之後再執行。

初始化:

這個階段主要是對類變數初始化,是執行類構造器的過程。

換句話說,只對static修飾的變數或語句進行初始化。

如果初始化一個類的時候,其父類尚未初始化,則優先初始化其父類。

如果同時包含多個靜態變數和靜態程式碼塊,則按照自上而下的順序依次執行。

5、雙親委派模型

什麼是類載入器?

類載入器就是根據指定全限定名稱將class檔案載入到JVM記憶體,轉為Class物件。如果站在JVM的角度來看,只存在兩種類載入器:

啟動類載入器(Bootstrap ClassLoader:由C++語言實現(針對HotSpot),負責將存放在<JAVA_HOME>\lib目錄或-Xbootclasspath引數指定的路徑中的類庫載入到記憶體中。

其他類載入器:由Java語言實現,繼承自抽象類ClassLoader。如:

擴充套件類載入器(Extension ClassLoader:負責載入<JAVA_HOME>\lib\ext目錄或java.ext.dirs系統變數指定的路徑中的所有類庫。

應用程式類載入器(Application ClassLoader):負責載入使用者類路徑(classpath)上的指定類庫,我們可以直接使用這個類載入器。一般情況,如果我們沒有自定義類載入器預設就是用這個載入器。

雙親委派模型:如果一個類載入器收到類載入的請求,它首先不會自己去嘗試載入這個類,而是把這個請求委派給父類載入器完成,如果父類載入器還存在其父類載入器,則進一步向上委託,依次遞迴,請求最終將到達頂層的啟動類載入器。每個類載入器都是如此,只有當父載入器在自己的搜尋範圍內找不到指定的類時(即ClassNotFoundException),子載入器才會嘗試自己去載入。 優勢: 安全性,不能隨意篡改系統api

防止重複載入

Java:

1、StringStringBufferStringBuilder的區別

2、java垃圾回收機制

概念:釋放掉在記憶體中已經沒用的物件。

GC執行的判斷方式:引用計數法 與 可達性分析演算法(引用鏈)。

3、手寫執行緒安全單例模式。

延伸 - 鎖為什麼寫在方法裡頭?

為了減少加鎖的次數,當intacne 例項化之後,可以直接返回,不需要再次加鎖

延伸 - 單例模式造成的記憶體洩漏問題?

public class SigleInstance {

    private static SigleInstance instance;

    public static SigleInstance getInstance() {

        if (instance == null) {

            syncronized(SigleInstance.class) {

                    if (instance == null) {
                            instance = new SingleInstance();
                    }
            }

       }
        return instance;
    }
}

4、HashMap Hashtable 的區別

/**Hashtable*/
public class Hashtable<K,V> extends Dictionary<K,V>
    implements Map<K,V>, Cloneable, java.io.Serializable {
   public synchronized V put(K key, V value) {
        // Make sure the value is not null
        if (value == null) {
            throw new NullPointerException();
        }    
    。。。。。
    }
}

/**HashMap*/

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {}

1、檢視原始碼可以看到,HashMap 繼承 AbstractMap, Hashtable 繼承Dictionary 但是都實現了Map、  Cloneable、 Serializable 

2、HashMap 的key  value 可以為null  ,但是 Hashtable 不行。

3、Hashtable 的方法是用synchronized 修飾的,執行緒安全,而 HashMap 執行緒不安全,所以,在單執行緒下,HashMap 執行效率更高。如果想讓 HashMap 執行緒安全,可以: Map map = Collections.synchronizedMap(new HashMap());

4、Hashtable和HashMap它們兩個內部實現方式的陣列的初始大小和擴容的方式不同。HashTable中hash陣列預設大小是11,增加的方式是 old*2+1。HashMap中hash陣列的預設大小是16,而且一定是2的指數。

延伸- ArrayList跟HashMap是否執行緒安全,如何保證執行緒安全?

都是執行緒不安全,通過 :Collections.synchronizedMap();  Collections.synchronizedList() 保證

5、Volley可以傳輸大資料嗎?為什麼?

不適合,Volley的網路請求執行緒池預設大小為4。意味著可以併發進行4個請求,大於4個,會排在佇列中volley中。為了提高請求處理的速度,採用了ByteArrayPool進行記憶體中的資料儲存的,如果下載大量的資料,這個儲存空間就會溢位,所以不適合大量的資料,但是由於他的這個儲存空間是記憶體中分配的,當儲存的時候優先從ByteArrayPool中取出一塊已經分配的記憶體區域, 不必每次存資料都要進行記憶體分配,而是先查詢緩衝池中有無適合的記憶體區域,如果有,直接拿來用,從而減少記憶體分配的次數 ,所以他比較適合大量的資料量少的網路資料互動情況。

6、Synchronized 和volatile 關鍵字的區別 

7、什麼是死鎖,產生死鎖的原因及必要條件

死鎖:

指兩個或兩個以上的程序在執行過程中,由於競爭資源或者由於彼此通訊而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的程序稱為死鎖程序。

死鎖產生的原因:

1 、因為系統資源不足。 

2、程序執行推進的順序不合適。 

3 、資源分配不當等

死鎖產生的的必要條件:

1、互斥條件:程序對所分配到的資源不允許其他程序進行訪問,若其他程序訪問該資源,只能等待,直至佔有該資源的程序使用完成後釋放該資源

2、請求和保持條件:程序獲得一定的資源之後,又對其他資源發出請求,但是該資源可能被其他程序佔有,此事請求阻塞,但又對自己獲得的資源保持不放

3、不可剝奪條件:是指程序已獲得的資源,在未完成使用之前,不可被剝奪,只能在使用完後自己釋放

4、環路等待條件:是指程序發生死鎖後,必然存在一個程序--資源之間的環形鏈

8、

9、

Android 相關:

1、LocalBroadcastManager的使用場景

解決  BroadcastReceiver的安全問題;

只能通過程式碼動態註冊。只在應用內傳送

2、string 轉換成 integer的方式及原理

3、Android應用程式的啟動過程  

4、 Bitmap影象畫素型別有哪幾種? 一個圖片所佔的記憶體大小

包括ALPHA_8、RGB_565、ARGB_4444、ARGB_8888  

A:透明度;RGB分別是Red、Green、Blue,三種原色

ARGB_8888:四個通道都是8位,一個畫素點佔8+8+8+8=32位,每個畫素佔用4個位元組;

ARGB_4444:四個通道都是4位,一個畫素點佔 4+4+4+4=16位,每個畫素佔用2個位元組;

RGB_565:沒有A通道,一個畫素點佔 5+6+5=16位,每個畫素佔用2個位元組,圖片失真小,但是沒有透明度;

ALPHA_8:只有A通道,一個畫素點佔  8 位,每個畫素佔用1個位元組大大小,只有透明度,沒有顏色值。

一張圖片Bitmap所佔用的記憶體 = 圖片長度 x 圖片寬度 x 一個畫素點佔用的位元組數

100*100 的ARGB_8888的圖片 記憶體= 100*100*4=40000byte

5、事件分發機制

6、View 的繪製原理

7、apk的release和debug版本的區別

8、Handler 原理相關? 一個執行緒能否建立多個Handler

可以建立多個Handler ,但是 多個handler通用一個messageQuene,並且只有一個Looper ,通過 msg.target 來標記 傳送的訊息 只有傳送訊息的handler才能響應

9、Activity的生命週期

延伸—橫豎屏切換的時候,Activity 各種情況下的生命週期

延伸—Activity上有Dialog的時候按Home鍵時的生命週期

10、

服務端:

1、如何判斷服務端是否支援斷點續傳?