Android知識(shí)點(diǎn)匯總

Java基礎(chǔ)

談?wù)?ArrayList 和 LinkList 的區(qū)別

  1. ArrayList是實(shí)現(xiàn)了基于動(dòng)態(tài)數(shù)組的數(shù)據(jù)結(jié)構(gòu)溯捆,LinkedList基于鏈表的數(shù)據(jù)結(jié)構(gòu)。

  2. 對(duì)于隨機(jī)訪問get和set,ArrayList覺得優(yōu)于LinkedList,因?yàn)長(zhǎng)inkedList要移動(dòng)指針。

  3. 對(duì)于新增和刪除操作add和remove,LinedList比較占優(yōu)勢(shì)琳袄,因?yàn)锳rrayList要移動(dòng)數(shù)據(jù)。

兩個(gè)都是多線程不安全纺酸。

From midNightHz

1窖逗、實(shí)現(xiàn)原理不一樣 ArrayList是基于數(shù)據(jù)的實(shí)現(xiàn),而LinkedList是基于鏈表的實(shí)現(xiàn)餐蔬,兩者的區(qū)別就是這兩種數(shù)據(jù)結(jié)構(gòu)的差別

2碎紊、初始化:ArrayList初始化時(shí)會(huì)初始化一個(gè)一定容量的數(shù)組,而linkedlist只是定義了鏈表的頭元素和尾元素

3用含、添加元素 在list尾端添加元素區(qū)別并不是太大矮慕,但ArrayList是基于Array來實(shí)現(xiàn)的,會(huì)遇到一個(gè)很蛋疼大問題就是擴(kuò)容問題

4啄骇、遍歷 :遍歷arraylist和linkedlist區(qū)別并不是很大

5痴鳄、查詢指定位置的元素 arraylist的查詢速度為0(1),而linkedlist則雙端的元素查詢快缸夹,中間的元素查詢慢

6痪寻、刪除元素 數(shù)組刪除元素是一個(gè)很蛋疼的問題,特別是移除數(shù)組頭端的元素 虽惭,需要一定當(dāng)前下標(biāo)元素后面的所有元素橡类,而linkedlist刪除雙端的元素則非常快 刪除中間的元素會(huì)比較慢(要遍歷查到為指定位置的元素)

From 吉文杰 jiwenjie

  1. 因?yàn)?Array 是基于索引 (index) 的數(shù)據(jù)結(jié)構(gòu)芽唇,它使用索引在數(shù)組中搜索和讀取數(shù)據(jù)是很快的顾画。Array 獲取數(shù)據(jù)的時(shí)間復(fù)雜度是 O(1), 但是要?jiǎng)h除數(shù)據(jù)卻是開銷很大的,因?yàn)檫@需要重排數(shù)組中的所有數(shù)據(jù)匆笤。

  2. 相對(duì)于 ArrayList , LinkedList 插入是更快的研侣。因?yàn)長(zhǎng)inkedList不像ArrayList 一樣,不需要改變數(shù)組的大小炮捧,也不需要在數(shù)組裝滿的時(shí)候要將所有的數(shù)據(jù)重新裝入一個(gè)新的數(shù)組庶诡,這是 ArrayList 最壞的一種情況,時(shí)間復(fù)雜度是 O(n) 咆课,而 LinkedList 中插入或刪除的時(shí)間復(fù)雜度僅為 O(1) 末誓。ArrayList 在插入數(shù)據(jù)時(shí)還需要更新索引(除了插入數(shù)組的尾部)扯俱。

  3. 類似于插入數(shù)據(jù),刪除數(shù)據(jù)時(shí)喇澡,LinkedList 也優(yōu)于 ArrayList 迅栅。

  4. LinkedList 需要更多的內(nèi)存,因?yàn)?ArrayList 的每個(gè)索引的位置是實(shí)際的數(shù)據(jù)撩幽,而 LinkedList 中的每個(gè)節(jié)點(diǎn)中存儲(chǔ)的是實(shí)際的數(shù)據(jù)和前后節(jié)點(diǎn)的位置( 一個(gè)LinkedList實(shí)例存儲(chǔ)了兩個(gè)值Node first 和 Node last 分別表示鏈表的其實(shí)節(jié)點(diǎn)和尾節(jié)點(diǎn)库继,每個(gè) Node 實(shí)例存儲(chǔ)了三個(gè)值:E item,Node next,Node pre)。

什么場(chǎng)景下更適宜使用LinkedList窜醉,而不用ArrayList

  1. 你的應(yīng)用不會(huì)隨機(jī)訪問數(shù)據(jù) 。因?yàn)槿绻阈枰狶inkedList中的第n個(gè)元素的時(shí)候艺谆,你需要從第一個(gè)元素順序數(shù)到第n個(gè)數(shù)據(jù)榨惰,然后讀取數(shù)據(jù)。

  2. 你的應(yīng)用更多的插入和刪除元素静汤,更少的讀取數(shù)據(jù)琅催。因?yàn)椴迦牒蛣h除元素不涉及重排數(shù)據(jù),所以它要比ArrayList要快虫给。

  3. 以上就是關(guān)于ArrayList和LinkedList的差別藤抡。你需要一個(gè)不同步的基于索引的數(shù)據(jù)訪問時(shí),請(qǐng)盡量使用ArrayList抹估。ArrayList很快缠黍,也很容易使用。但是要記得要給定一個(gè)合適的初始大小药蜻,盡可能的減少更改數(shù)組的大小瓷式。

簡(jiǎn)述HashMap工作原理

HashMap是基于hashing算法的原理,通過put(key语泽,value)和get(key)方法儲(chǔ)存和獲取值的贸典。

  1. 存:我們將鍵值對(duì)K/V 傳遞給put()方法,它調(diào)用K對(duì)象的hashCode()方法來計(jì)算hashCode從而得到bucket位置踱卵,之后儲(chǔ)存Entry對(duì)象廊驼。(HashMap是在bucket中儲(chǔ)存 鍵對(duì)象 和 值對(duì)象,作為Map.Entry)

  2. 韧锷啊:獲取對(duì)象時(shí)妒挎,我們傳遞 鍵給get()方法,然后調(diào)用K的hashCode()方法從而得到hashCode進(jìn)而獲取到bucket位置班利,再調(diào)用K的equals()方法從而確定鍵值對(duì)饥漫,返回值對(duì)象。

  3. 碰撞:當(dāng)兩個(gè)對(duì)象的hashcode相同時(shí)罗标,它們的bucket位置相同庸队,‘碰撞’就會(huì)發(fā)生积蜻。如何解決,就是利用鏈表結(jié)構(gòu)進(jìn)行存儲(chǔ)彻消,即HashMap使用LinkedList存儲(chǔ)對(duì)象竿拆。但是當(dāng)鏈表長(zhǎng)度大于8(默認(rèn))時(shí),就會(huì)把鏈表轉(zhuǎn)換為紅黑樹宾尚,在紅黑樹中執(zhí)行插入獲取操作丙笋。

  4. 擴(kuò)容:如果HashMap的大小超過了負(fù)載因子定義的容量,就會(huì)進(jìn)行擴(kuò)容煌贴。默認(rèn)負(fù)載因子為0.75御板。就是說,當(dāng)一個(gè)map填滿了75%的bucket時(shí)候牛郑,將會(huì)創(chuàng)建原來HashMap大小的兩倍的bucket數(shù)組(jdk1.6怠肋,但不超過最大容量),來重新調(diào)整map的大小淹朋,并將原來的對(duì)象放入新的bucket數(shù)組中笙各。這個(gè)過程叫作rehashing,因?yàn)樗{(diào)用hash方法找到新的bucket位置础芍。

[圖片上傳失敗...(image-535329-1564397833219)]

補(bǔ)充答案
From midNightHz

從前有個(gè)叫媽個(gè)蛋村杈抢,村里有個(gè)地主,有錢又有顏仑性,有能力可以娶很多妻妾惶楼,但是怎么樣才能快速找到想要的妻妾呢,于是這個(gè)地主想了一個(gè)辦法虏缸;

1鲫懒、先建16個(gè)房子(標(biāo)上0-15),為什么是16個(gè)呢刽辙,算命的說的

2窥岩、每娶到一個(gè)妻子(put),就根據(jù)妻子的生日宰缤,每年的第幾天(hash值)算出要讓這個(gè)妻子住在哪個(gè)房間颂翼,具體算法是這樣的 hash%16 等于0就住0號(hào)房;

3慨灭、問題來了朦乏,這個(gè)有錢富豪娶了兩個(gè)妻子,兩個(gè)生日不同氧骤,但是要住同一個(gè)房間怎么辦(hash碰撞)這個(gè)有錢的地主想了一個(gè)辦法呻疹,讓這些住同一個(gè)房間的妻子根據(jù)生日排個(gè)大小(二叉樹)

4筹陵、過來一段時(shí)間以后刽锤,這位有錢的地主娶了12房姨太太镊尺,他想著房子快不夠住了,怎么辦并思,又建了16個(gè)房子(hashmap擴(kuò)容)庐氮,然后重新安排他們的住所

From 吉文杰 jiwenjie

HashMap的工作原理

  1. 什么時(shí)候會(huì)使用HashMap?他有什么特點(diǎn)宋彼?

是基于Map接口的實(shí)現(xiàn)弄砍,存儲(chǔ)鍵值對(duì)時(shí),它可以接收null的鍵值输涕,是非同步的音婶,HashMap存儲(chǔ)著Entry(hash, key, value, next)對(duì)象。

  1. 你知道HashMap的工作原理嗎莱坎?

通過hash的方法桃熄,通過put和get存儲(chǔ)和獲取對(duì)象。存儲(chǔ)對(duì)象時(shí)型奥,我們將K/V傳給put方法時(shí),它調(diào)用hashCode計(jì)算hash從而得到bucket位置碉京,進(jìn)一步存儲(chǔ)厢汹,HashMap會(huì)根據(jù)當(dāng)前bucket的占用情況自動(dòng)調(diào)整容量(超過Load Facotr則resize為原來的2倍)。獲取對(duì)象時(shí)谐宙,我們將K傳給get烫葬,它調(diào)用hashCode計(jì)算hash從而得到bucket位置,并進(jìn)一步調(diào)用equals()方法確定鍵值對(duì)凡蜻。如果發(fā)生碰撞的時(shí)候搭综,Hashmap通過鏈表將產(chǎn)生碰撞沖突的元素組織起來,在Java 8中划栓,如果一個(gè)bucket中碰撞沖突的元素超過某個(gè)限制(默認(rèn)是8)兑巾,則使用紅黑樹來替換鏈表,從而提高速度忠荞。

  1. 你知道get和put的原理嗎蒋歌?equals()和hashCode()的都有什么作用?

通過對(duì)key的hashCode()進(jìn)行hashing委煤,并計(jì)算下標(biāo)( n-1 & hash)堂油,從而獲得buckets的位置。如果產(chǎn)生碰撞碧绞,則利用key.equals()方法去鏈表或樹中去查找對(duì)應(yīng)的節(jié)點(diǎn)

  1. 你知道hash的實(shí)現(xiàn)嗎府框?為什么要這樣實(shí)現(xiàn)?

在Java 1.8的實(shí)現(xiàn)中讥邻,是通過hashCode()的高16位異或低16位實(shí)現(xiàn)的:(h = k.hashCode()) ^ (h >>> 16)迫靖,主要是從速度院峡、功效、質(zhì)量來考慮的袜香,這么做可以在bucket的n比較小的時(shí)候撕予,也能保證考慮到高低bit都參與到hash的計(jì)算中,同時(shí)不會(huì)有太大的開銷蜈首。

  1. 如果HashMap的大小超過了負(fù)載因子(load factor)定義的容量实抡,怎么辦?

如果超過了負(fù)載因子(默認(rèn)0.75)欢策,則會(huì)重新resize一個(gè)原來長(zhǎng)度兩倍的HashMap吆寨,并且重新調(diào)用hash方法。

關(guān)于Java集合的小抄中是這樣描述的:

以Entry[]數(shù)組實(shí)現(xiàn)的哈希桶數(shù)組踩寇,用Key的哈希值取模桶數(shù)組的大小可得到數(shù)組下標(biāo)啄清。

插入元素時(shí),如果兩條Key落在同一個(gè)桶(比如哈希值1和17取模16后都屬于第一個(gè)哈希桶)俺孙,我們稱之為哈希沖突辣卒。

JDK的做法是鏈表法,Entry用一個(gè)next屬性實(shí)現(xiàn)多個(gè)Entry以單向鏈表存放睛榄。查找哈希值為17的key時(shí)荣茫,先定位到哈希桶,然后鏈表遍歷桶里所有元素场靴,逐個(gè)比較其Hash值然后key值啡莉。

在JDK8里,新增默認(rèn)為8的閾值旨剥,當(dāng)一個(gè)桶里的Entry超過閥值咧欣,就不以單向鏈表而以紅黑樹來存放以加快Key的查找速度。

當(dāng)然轨帜,最好還是桶里只有一個(gè)元素魄咕,不用去比較。所以默認(rèn)當(dāng)Entry數(shù)量達(dá)到桶數(shù)量的75%時(shí)阵谚,哈希沖突已比較嚴(yán)重蚕礼,就會(huì)成倍擴(kuò)容桶數(shù)組,并重新分配所有原來的Entry梢什。擴(kuò)容成本不低奠蹬,所以也最好有個(gè)預(yù)估值。

取模用與操作(hash & (arrayLength-1))會(huì)比較快嗡午,所以數(shù)組的大小永遠(yuǎn)是2的N次方囤躁, 你隨便給一個(gè)初始值比如17會(huì)轉(zhuǎn)為32。默認(rèn)第一次放入元素時(shí)的初始值是16。

iterator()時(shí)順著哈希桶數(shù)組來遍歷狸演,所以看起來是個(gè)亂序

接口和抽象類有什么區(qū)別

  1. 共同點(diǎn)

是上層的抽象層言蛇。

都不能被實(shí)例化。

都能包含抽象的方法宵距,這些抽象的方法用于描述類具備的功能腊尚,但是不比提供具體的實(shí)現(xiàn)。

  1. 區(qū)別

在抽象類中可以寫非抽象的方法满哪,從而避免在子類中重復(fù)書寫他們婿斥,這樣可以提高代碼的復(fù)用性,這是抽象類的優(yōu)勢(shì)哨鸭,接口中只能有抽象的方法民宿。

一個(gè)類只能繼承一個(gè)直接父類,這個(gè)父類可以是具體的類也可是抽象類像鸡,但是一個(gè)類可以實(shí)現(xiàn)多個(gè)接口活鹰。

From jiwenjie

  1. 默認(rèn)的方法實(shí)現(xiàn) 抽象類可以有默認(rèn)的方法實(shí)現(xiàn)完全是抽象的。接口根本不存在方法的實(shí)現(xiàn)只估。

抽象類中可以有已經(jīng)實(shí)現(xiàn)了的方法志群,也可以有被abstract修飾的方法(抽象方法),因?yàn)榇嬖诔橄蠓椒ɑ赘疲栽擃惐仨毷浅橄箢惱抵邸5墙涌谝笾荒馨橄蠓椒ǎ橄蠓椒ㄊ侵笡]有實(shí)現(xiàn)的方法夸楣。所以就不能像抽象類那么無賴了,接口就根本不能存在方法的實(shí)現(xiàn)子漩。實(shí)現(xiàn) 抽象類使用extends關(guān)鍵字來繼承抽象類豫喧。如果子類不是抽象類的話,它需要提供抽象類中所有聲明的方法的實(shí)現(xiàn)幢泼。子類使用關(guān)鍵字implements來實(shí)現(xiàn)接口紧显。它需要提供接口中所有聲明的方法的實(shí)現(xiàn)。抽象類雖然不能實(shí)例化來使用缕棵,但是可以被繼承孵班,讓子類來具體實(shí)現(xiàn)父類的所有抽象方法。

  1. 接口的實(shí)現(xiàn)招驴,通過implements關(guān)鍵字篙程。實(shí)現(xiàn)該接口的類,必須把接口中的所有方法給實(shí)現(xiàn)别厘。不能再推給下一代虱饿。

  2. 抽象類可以有構(gòu)造器,而接口不能有構(gòu)造器

  3. 抽象方法可以有public、protected和default這些修飾符

  4. 接口方法默認(rèn)修飾符是public氮发。你不可以使用其它修飾符渴肉。

  5. 抽象類在java語(yǔ)言中所表示的是一種繼承關(guān)系,一個(gè)子類只能存在一個(gè)父類爽冕,但是可以存在多個(gè)接口仇祭。

  6. 如果你往抽象類中添加新的方法,你可以給它提供默認(rèn)的實(shí)現(xiàn)颈畸。因此你不需要改變你現(xiàn)在的代碼乌奇。 如果你往接口中添加方法,那么你必須改變實(shí)現(xiàn)該接口的類承冰。

From midNightHz

1华弓、抽象類是個(gè)類,而接口就是一個(gè)接口困乒,一個(gè)是三個(gè)字寂屏,一個(gè)是兩個(gè)字

2、抽象類只能單繼承娜搂,而接口可以多實(shí)現(xiàn)

3迁霎、抽象類可以有成員變量(私有 共有and so on);接口只可能有常量,而且都public

4百宇、抽象類可以有所有的方法可以用所有的修飾符來修飾 public private protected static 考廉,可以包含0-N個(gè)抽象方法;接口的方法都是public的携御,JDK1.8接口允許有一個(gè)static方法和多個(gè)default方法

From Moosphan

大體區(qū)別如下:

  • 抽象類可以提供成員方法的實(shí)現(xiàn)細(xì)節(jié)昌粤,而接口中只能存在 public 抽象方法;

  • 抽象類中的成員變量可以是各種類型的啄刹,而接口中的成員變量只能是 public static final 類型的涮坐;

  • 接口中不能含有構(gòu)造器、靜態(tài)代碼塊以及靜態(tài)方法誓军,而抽象類可以有構(gòu)造器袱讹、靜態(tài)代碼塊和靜態(tài)方法;

  • 一個(gè)類只能繼承一個(gè)抽象類昵时,而一個(gè)類卻可以實(shí)現(xiàn)多個(gè)接口捷雕;

  • 抽象類訪問速度比接口速度要快,因?yàn)榻涌谛枰獣r(shí)間去尋找在類中具體實(shí)現(xiàn)的方法壹甥;

  • 如果你往抽象類中添加新的方法救巷,你可以給它提供默認(rèn)的實(shí)現(xiàn)。因此你不需要改變你現(xiàn)在的代碼句柠。如果你往接口中添加方法征绸,那么你必須改變實(shí)現(xiàn)該接口的類久橙;

  • 此外,Java 8 之后的接口可以有默認(rèn)方法實(shí)現(xiàn)管怠,通過 default 關(guān)鍵字表明即可淆衷。

From safier

抽象類是用來捕捉子類的通用特性的 。它不能被實(shí)例化渤弛,只能被用作子類的超類祝拯。抽象類是被用來創(chuàng)建繼承層級(jí)里子類的模板。以JDK中的GenericServlet為例:


public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {

    // abstract method

    abstract void service(ServletRequest req, ServletResponse res);

    void init() {

        // Its implementation

    }

    // other method related to Servlet

}

當(dāng)HttpServlet類繼承GenericServlet時(shí)她肯,它提供了service方法的實(shí)現(xiàn):


public class HttpServlet extends GenericServlet {

    void service(ServletRequest req, ServletResponse res) {

        // implementation

    }

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {

        // Implementation

    }

    protected void doPost(HttpServletRequest req, HttpServletResponse resp) {

        // Implementation

    }

    // some other methods related to HttpServlet

}

接口

接口是抽象方法的集合佳头。如果一個(gè)類實(shí)現(xiàn)了某個(gè)接口,那么它就繼承了這個(gè)接口的抽象方法晴氨。這就像契約模式康嘉,如果實(shí)現(xiàn)了這個(gè)接口,那么就必須確保使用這些方法籽前。接口只是一種形式亭珍,接口自身不能做任何事情。以Externalizable接口為例:


public interface Externalizable extends Serializable {

    void writeExternal(ObjectOutput out) throws IOException;

    void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;

}

當(dāng)你實(shí)現(xiàn)這個(gè)接口時(shí)枝哄,你就需要實(shí)現(xiàn)上面的兩個(gè)方法:


public class Employee implements Externalizable {

    int employeeId;

    String employeeName;

    @Override

    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {

        employeeId = in.readInt();

        employeeName = (String) in.readObject();

    }

    @Override

    public void writeExternal(ObjectOutput out) throws IOException {

        out.writeInt(employeeId);

        out.writeObject(employeeName);

    }

}

抽象類和接口的對(duì)比

| 參數(shù) | 抽象類 | 接口 |

| ------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |

| 默認(rèn)的方法實(shí)現(xiàn) | 它可以有默認(rèn)的方法實(shí)現(xiàn) | 接口完全是抽象的肄梨。它根本不存在方法的實(shí)現(xiàn) |

| 實(shí)現(xiàn) | 子類使用extends關(guān)鍵字來繼承抽象類。如果子類不是抽象類的話挠锥,它需要提供抽象類中所有聲明的方法的實(shí)現(xiàn)众羡。 | 子類使用關(guān)鍵字implements來實(shí)現(xiàn)接口。它需要提供接口中所有聲明的方法的實(shí)現(xiàn) |

| 構(gòu)造器 | 抽象類可以有構(gòu)造器 | 接口不能有構(gòu)造器 |

| 與正常Java類的區(qū)別 | 除了你不能實(shí)例化抽象類之外蓖租,它和普通Java類沒有任何區(qū)別 | 接口是完全不同的類型 |

| 訪問修飾符 | 抽象方法可以有public粱侣、protected和default這些修飾符 | 接口方法默認(rèn)修飾符是public。你不可以使用其它修飾符蓖宦。 |

| main方法 | 抽象方法可以有main方法并且我們可以運(yùn)行它 | 接口沒有main方法甜害,因此我們不能運(yùn)行它 |

| 多繼承 | 抽象方法可以繼承一個(gè)類和實(shí)現(xiàn)多個(gè)接口 | 接口只可以繼承一個(gè)或多個(gè)其它接口 |

| 速度 | 它比接口速度要快 | 接口是稍微有點(diǎn)慢的,因?yàn)樗枰獣r(shí)間去尋找在類中實(shí)現(xiàn)的方法球昨。 |

| 添加新方法 | 如果你往抽象類中添加新的方法,你可以給它提供默認(rèn)的實(shí)現(xiàn)眨攘。因此你不需要改變你現(xiàn)在的代碼主慰。 | 如果你往接口中添加方法,那么你必須改變實(shí)現(xiàn)該接口的類鲫售。 |

什么時(shí)候使用抽象類和接口

  • 如果你擁有一些方法并且想讓它們中的一些有默認(rèn)實(shí)現(xiàn)共螺,那么使用抽象類吧。

  • 如果你想實(shí)現(xiàn)多重繼承情竹,那么你必須使用接口藐不。由于Java不支持多繼承,子類不能夠繼承多個(gè)類,但可以實(shí)現(xiàn)多個(gè)接口雏蛮。因此你就可以使用接口來解決它涎嚼。

  • 如果基本功能在不斷改變,那么就需要使用抽象類挑秉。如果不斷改變基本功能并且使用接口法梯,那么就需要改變所有實(shí)現(xiàn)了該接口的類。

Java8中的默認(rèn)方法和靜態(tài)方法

Oracle已經(jīng)開始嘗試向接口中引入默認(rèn)方法和靜態(tài)方法犀概,以此來減少抽象類和接口之間的差異×⒀疲現(xiàn)在,我們可以為接口提供默認(rèn)實(shí)現(xiàn)的方法了并且不用強(qiáng)制子類來實(shí)現(xiàn)它姻灶。

談?wù)?Java 中多線程實(shí)現(xiàn)的幾種方式

Java中多線程實(shí)現(xiàn)的方式主要有三種:

  1. 繼承Thread類

  2. 實(shí)現(xiàn)Runnable接口

  3. 使用ExecutorService铛绰、Callable、Future實(shí)現(xiàn)有返回結(jié)果的多線程

其中前兩種方式線程執(zhí)行完沒有返回值产喉,只有最后一種是帶返回值的捂掰。

繼承Thread類實(shí)現(xiàn)多線程:

繼承Thread類本質(zhì)上也是實(shí)現(xiàn)Tunnable接口的一個(gè)實(shí)例,他代表一個(gè)線程的實(shí)例镊叁,并且啟動(dòng)線程的唯一方法是通過Thread類的start()方法尘颓,start()方法是一個(gè)native方法,他將啟動(dòng)一個(gè)新線程晦譬,并執(zhí)行run( )方法疤苹。

實(shí)現(xiàn)Runnable接口方式實(shí)現(xiàn)多線程:

實(shí)例化一個(gè)Thread對(duì)象,并傳入實(shí)現(xiàn)的Runnable接口敛腌,當(dāng)傳入一個(gè)Runnable target參數(shù)給Thread后卧土,Thraed的run()方法就會(huì)調(diào)用target.run( );

使用ExecutorService、Callable像樊、Future實(shí)現(xiàn)有返回結(jié)果的多線程:

可返回值的任務(wù)必須實(shí)現(xiàn)Callable接口尤莺,類似的無返回值的任務(wù)必須實(shí)現(xiàn)Runnable接口,執(zhí)行Callable任務(wù)后生棍,可以獲取一個(gè)Future的對(duì)象颤霎,在該對(duì)象上調(diào)用get就可以獲取到Callable任務(wù)返回的Object了,在結(jié)合線程池接口ExecutorService就可以實(shí)現(xiàn)有返回結(jié)果的多線程涂滴。

繼承 Thread 類本身


public class Test {

    public static void main(String[] args) {

        MyThread mt = new MyThread();

        mt.start();

    }

}

class MyThread extends Thread {

    public void run() {

        System.out.println(Thread.currentThread().getName());

    }

}

#實(shí)現(xiàn)Runnable接口

*用法需要在外層包裹一層 Thread *


public class Test {

    public static void main(String[] args) {

        MyRunnable mr = new MyRunnable();

        new Thread(mr).start();

    }

}

class MyRunnable implements Runnable {

    public void run() {

        System.out.println(Thread.currentThread().getName());

    }

}

#實(shí)現(xiàn) Callable 接口

比較少見友酱,不常用

需要實(shí)現(xiàn)的是 call() 方法

代碼拷過來的,確實(shí)沒用過


public class Test {

    public static void main(String[] args) throws Exception {

        ExecutorService es = Executors.newSingleThreadExecutor();

        // 自動(dòng)在一個(gè)新的線程上啟動(dòng) MyCallable柔纵,執(zhí)行 call 方法

        Future<Integer> f = es.submit(new MyCallable());

        // 當(dāng)前 main 線程阻塞缔杉,直至 future 得到值

        System.out.println(f.get());

        es.shutdown();

    }

}

class MyCallable implements Callable<Integer> {

    public Integer call() {

        System.out.println(Thread.currentThread().getName());

        try {

            Thread.sleep(2000);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        return 123;

    }

}

String,StringBuilder搁料,StringBuffer的區(qū)別

  1. 可變不可變

String:字符串常量或详,在修改時(shí)不會(huì)改變自身系羞;若修改,等于重新生成新的字符串對(duì)象霸琴。

StringBuffer:在修改時(shí)會(huì)改變對(duì)象自身椒振,每次操作都是對(duì) StringBuffer 對(duì)象本身進(jìn)行修改,不是生成新的對(duì)

象沈贝;使用場(chǎng)景:對(duì)字符串經(jīng)常改變情況下杠人,主要方法:append(),insert()等宋下。

  1. 線程是否安全

String:對(duì)象定義后不可變嗡善,線程安全。

StringBuffer:是線程安全的(對(duì)調(diào)用方法加入同步鎖)学歧,執(zhí)行效率較慢罩引,適用于多線程下操作字符串緩沖區(qū)

大量數(shù)據(jù)。

StringBuilder:是線程不安全的枝笨,適用于單線程下操作字符串緩沖區(qū)大量數(shù)據(jù)袁铐。

  1. 共同點(diǎn)

StringBuilder 與 StringBuffer 有公共父類 AbstractStringBuilder(抽象類)。

StringBuilder横浑、StringBuffer 的方法都會(huì)調(diào)用 AbstractStringBuilder 中的公共方法剔桨,如 super.append(...)耗美。

只是 StringBuffer 會(huì)在方法上加 synchronized 關(guān)鍵字诽凌,進(jìn)行同步。最后具垫,如果程序不是多線程的欺冀,那么使用

StringBuilder 效率高于 StringBuffer树绩。

HashMap和Hashtable的區(qū)別

參考答案
  1. HashMap是map接口的子類,是將鍵映射到值的對(duì)象隐轩,其中鍵和值都是對(duì)象饺饭,并且不能包含重復(fù)鍵,但可以包含重復(fù)值职车。HashMap允許null key和null value瘫俊,而hashtable不允許。

  2. HashMap是Hashtable的輕量級(jí)實(shí)現(xiàn)(非線程安全的實(shí)現(xiàn))悴灵,他們都完成了Map接口扛芽,由于非線程安全,效率上可能高于Hashtable称勋。

  3. HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey涯竟。

  4. Hashtable繼承自Dictionary類赡鲜,而HashMap是Java1.2引進(jìn)的Map interface的一個(gè)實(shí)現(xiàn)空厌。

  5. Hashtable的方法是Synchronize的,而HashMap不是银酬,在多個(gè)線程訪問Hashtable時(shí)嘲更,不需要自己為它的方法實(shí)現(xiàn)同步,而HashMap 就必須為之提供外同步揩瞪。但是如果使用Java 5或以上的話赋朦,可以用ConcurrentHashMap代替Hashtable。

  6. Hashtable和HashMap采用的hash/rehash算法都大概一樣李破,所以性能不會(huì)有很大的差宠哄。

談?wù)勀銓?duì)java三大特性的理解

封裝

封裝最好理解了。封裝是面向?qū)ο蟮奶卣髦秽凸ィ菍?duì)象和類概念的主要特性毛嫉。

封裝,也就是把客觀事物封裝成抽象的類妇菱,并且類可以把自己的數(shù)據(jù)和方法只讓可信的類或者對(duì)象操作承粤,對(duì)不可信的進(jìn)行信息隱藏。

繼承

面向?qū)ο缶幊?(OOP) 語(yǔ)言的一個(gè)主要功能就是“繼承”闯团。繼承是指這樣一種能力:它可以使用現(xiàn)有類的所有功能辛臊,并在無需重新編寫原來的類的情況下對(duì)這些功能進(jìn)行擴(kuò)展。

通過繼承創(chuàng)建的新類稱為“子類”或“派生類”房交。

被繼承的類稱為“基類”彻舰、“父類”或“超類”。

繼承的過程涌萤,就是從一般到特殊的過程淹遵。

多態(tài)

多態(tài)性(polymorphisn)是允許你將父對(duì)象設(shè)置成為和一個(gè)或更多的他的子對(duì)象相等的技術(shù),賦值之后负溪,父對(duì)象就可以根據(jù)當(dāng)前賦值給它的子對(duì)象的特性以不同的方式運(yùn)作透揣。簡(jiǎn)單的說,就是一句話:允許將子類類型的指針賦值給父類類型的指針川抡。

實(shí)現(xiàn)多態(tài)辐真,有二種方式,覆蓋崖堤,重載侍咱。

覆蓋,是指子類重新定義父類的虛函數(shù)的做法密幔。

重載楔脯,是指允許存在多個(gè)同名函數(shù),而這些函數(shù)的參數(shù)表不同(或許參數(shù)個(gè)數(shù)不同胯甩,或許參數(shù)類型不同昧廷,或許兩者都不同)堪嫂。

JVM

談?wù)凧VM的內(nèi)存結(jié)構(gòu)和內(nèi)存分配

Java內(nèi)存模型

Java虛擬機(jī)將其管轄的內(nèi)存大致分三個(gè)邏輯部分:方法區(qū)(Method Area)、Java棧和Java堆木柬。

方法區(qū)是靜態(tài)分配的皆串,編譯器將變量綁定在某個(gè)存儲(chǔ)位置上,而且這些綁定不會(huì)在運(yùn)行時(shí)改變眉枕。

Java Stack是一個(gè)邏輯概念恶复,特點(diǎn)是后進(jìn)先出。一個(gè)棧的空間可能是連續(xù)的速挑,也可能是不連續(xù)的谤牡。

Java堆分配(heap allocation)意味著以隨意的順序,在運(yùn)行時(shí)進(jìn)行存儲(chǔ)空間分配和收回的內(nèi)存管理模型梗摇。

java內(nèi)存分配

基礎(chǔ)數(shù)據(jù)類型直接在椡赜矗空間分配;

方法的形式參數(shù),直接在椓媸冢空間分配断序,當(dāng)方法調(diào)用完成后從棧空間回收;

引用數(shù)據(jù)類型糜烹,需要用new來創(chuàng)建违诗,既在棧空間分配一個(gè)地址空間疮蹦,又在堆空間分配對(duì)象的類變量;

方法的引用參數(shù)诸迟,在棧空間分配一個(gè)地址空間愕乎,并指向堆空間的對(duì)象區(qū)阵苇,當(dāng)方法調(diào)用完后從棧空間回收;

局部變量 new 出來時(shí)感论,在椛鹣睿空間和堆空間中分配空間,當(dāng)局部變量生命周期結(jié)束后比肄,椏旃ⅲ空間立刻被回收,堆空間區(qū)域等待GC回收;

方法調(diào)用時(shí)傳入的實(shí)際參數(shù)芳绩,先在椣坪ィ空間分配,在方法調(diào)用完成后從椡咨空間釋放;

字符串常量在 DATA 區(qū)域分配 搪花,this 在堆空間分配;

數(shù)組既在棧空間分配數(shù)組名稱, 又在堆空間分配數(shù)組實(shí)際的大写楦汀丁稀!

From BelieveFrank

內(nèi)存結(jié)構(gòu)
程序計(jì)數(shù)器

程序計(jì)數(shù)器(Program Counter Register)是一塊較小的內(nèi)存空間,它的作用可以看做是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器倚聚。在虛擬機(jī)的概念模型里(僅是概念模型,各種虛擬機(jī)可能會(huì)通過一些更高效的方式去實(shí)現(xiàn))凿可,字節(jié)碼解釋器工作時(shí)就是通過改變這個(gè)計(jì)數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令惑折,分支、循環(huán)枯跑、跳轉(zhuǎn)惨驶、異常處理、線程恢復(fù)等基礎(chǔ)功能都需要依賴這個(gè)計(jì)數(shù)器來完成敛助。 由于Java虛擬機(jī)的多線程是通過線程輪流切換并分配處理器執(zhí)行時(shí)間的方式來實(shí)現(xiàn)的粗卜,在任何一個(gè)確定的時(shí)刻,一個(gè)處理器(對(duì)于多核處理器來說是一個(gè)內(nèi)核)只會(huì)執(zhí)行一條線程中的指令纳击。因此续扔,為了線程切換后能恢復(fù)到正確的執(zhí)行位置,每條線程都需要有一個(gè)獨(dú)立的程序計(jì)數(shù)器焕数,各條線程之間的計(jì)數(shù)器互不影響纱昧,獨(dú)立存儲(chǔ),我們稱這類內(nèi)存區(qū)域?yàn)椤熬€程私有”的內(nèi)存堡赔。

如果線程正在執(zhí)行的是一個(gè)Java方法识脆,這個(gè)計(jì)數(shù)器記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的地址;如果正在執(zhí)行的是Natvie方法善已,這個(gè)計(jì)數(shù)器值則為空(Undefined)灼捂。

此內(nèi)存區(qū)域是唯一一個(gè)在Java虛擬機(jī)規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域。

Java虛擬機(jī)棧

虛擬機(jī)棧描述的是Java方法執(zhí)行的內(nèi)存模型换团,每個(gè)方法在執(zhí)行的同時(shí)都會(huì)創(chuàng)建一個(gè)棧幀(Stack Frame)用于存儲(chǔ)局部變量表悉稠、操作數(shù)棧、動(dòng)態(tài)鏈接啥寇、方法出口等信息偎球。每個(gè)方法從調(diào)用直至執(zhí)行完成的過程,就對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)中入棧到出棧的過程辑甜。

在Java虛擬機(jī)規(guī)范中衰絮,對(duì)這個(gè)區(qū)域規(guī)定了兩種異常狀況:如果線程請(qǐng)求的棧深度大于虛擬機(jī)所允許的深度,將拋出StackOverflowError異常磷醋;如果虛擬機(jī)椕担可以動(dòng)態(tài)擴(kuò)展(當(dāng)前大部分的Java虛擬機(jī)都可動(dòng)態(tài)擴(kuò)展,只不過Java虛擬機(jī)規(guī)范中也允許固定長(zhǎng)度的虛擬機(jī)棧)邓线,當(dāng)擴(kuò)展時(shí)無法申請(qǐng)到足夠的內(nèi)存時(shí)會(huì)拋出OutOfMemoryError異常淌友。

本方法棧

本地方法棧(Native Method Stacks)與虛擬機(jī)棧所發(fā)揮的作用是非常相似的煌恢,其區(qū)別不過是虛擬機(jī)棧為虛擬機(jī)執(zhí)行Java方法(也就是字節(jié)碼)服務(wù),而本地方法棧則是為虛擬機(jī)使用到的Native方法服務(wù)震庭。虛擬機(jī)規(guī)范中對(duì)本地方法棧中的方法使用的語(yǔ)言瑰抵、使用方式與數(shù)據(jù)結(jié)構(gòu)并沒有強(qiáng)制規(guī)定,因此具體的虛擬機(jī)可以自由實(shí)現(xiàn)它器联。甚至有的虛擬機(jī)(譬如Sun HotSpot虛擬機(jī))直接就把本地方法棧和虛擬機(jī)棧合二為一二汛。與虛擬機(jī)棧一樣,本地方法棧區(qū)域也會(huì)拋出StackOverflowError和OutOfMemoryError異常拨拓。

Java堆

對(duì)于大多數(shù)應(yīng)用來說肴颊,Java堆(Java Heap)是Java虛擬機(jī)所管理的內(nèi)存中最大的一塊。Java堆是被所有線程共享的一塊內(nèi)存區(qū)域渣磷,在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建婿着。此內(nèi)存區(qū)域的唯一目的就是存放對(duì)象實(shí)例,幾乎所有的對(duì)象實(shí)例都在這里分配內(nèi)存醋界。(棧上分配竟宋、標(biāo)量替換會(huì)導(dǎo)致對(duì)象不分配在堆內(nèi)存中)

Java堆是垃圾收集器管理的主要區(qū)域,因此很多時(shí)候也被稱做“GC堆”形纺。如果從內(nèi)存回收的角度看袜硫,由于現(xiàn)在收集器基本都是采用的分代收集算法,所以Java堆中還可以細(xì)分為:新生代和老年代挡篓;再細(xì)致一點(diǎn)的有Eden空間婉陷、From Survivor空間、To Survivor空間等官研。

根據(jù)Java虛擬機(jī)規(guī)范的規(guī)定秽澳,Java堆可以處于物理上不連續(xù)的內(nèi)存空間中,只要邏輯上是連續(xù)的即可戏羽,就像我們的磁盤空間一樣担神。在實(shí)現(xiàn)時(shí),既可以實(shí)現(xiàn)成固定大小的始花,也可以是可擴(kuò)展的妄讯,不過當(dāng)前主流的虛擬機(jī)都是按照可擴(kuò)展來實(shí)現(xiàn)的(通過-Xmx和-Xms控制)。

如果在堆中沒有內(nèi)存完成實(shí)例分配酷宵,并且堆也無法再擴(kuò)展時(shí)亥贸,將會(huì)拋出OutOfMemoryError異常。

方法區(qū)

方法區(qū)(Method Area)與Java堆一樣浇垦,是各個(gè)線程共享的內(nèi)存區(qū)域炕置,它用于存儲(chǔ)已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量朴摊、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)默垄。雖然Java虛擬機(jī)規(guī)范把方法區(qū)描述為堆的一個(gè)邏輯部分,但是它卻有一個(gè)別名叫做Non-Heap(非堆)甚纲,目的應(yīng)該是與Java堆區(qū)分開來口锭。

根據(jù)Java虛擬機(jī)規(guī)范的規(guī)定,當(dāng)方法區(qū)無法滿足內(nèi)存分配需求時(shí)介杆,將拋出OutOfMemoryError異常讹弯。

內(nèi)存分配策略
對(duì)象優(yōu)先在Eden分配

大多數(shù)情況下,對(duì)象在新生代Eden去中分配这溅,(注:java堆中的新生代可分為Eden區(qū)和兩個(gè)Survivor區(qū)),當(dāng)Eden區(qū)中沒有足夠的空間進(jìn)行分配時(shí)棒仍,虛擬機(jī)將發(fā)起一次Minor GC悲靴。

Minor GC 和 Full GC的區(qū)別

  • 新生代GC(Minor GC):指的是發(fā)生在新生代中的垃圾收集動(dòng)作,java對(duì)象的創(chuàng)建和回收非常頻繁莫其,所以Mnior GC非常頻繁癞尚,一般回收速度也比較快。
  • 老年代GC(Major GC/Full FC):指發(fā)生在老年代中的GC乱陡,出現(xiàn)了Major GC經(jīng)常會(huì)伴隨至少一次的Minor GC(并非絕對(duì))浇揩,Major GC的速度一般會(huì)比Minor GC慢10倍以上。
大對(duì)象直接進(jìn)入老年代

大對(duì)象是指憨颠,需要大量連續(xù)內(nèi)存空間的java對(duì)象(寫程序的時(shí)候應(yīng)該避免“短命大對(duì)象”)胳徽,經(jīng)常出現(xiàn)大對(duì)象,容易導(dǎo)致內(nèi)存還有不少空間時(shí)爽彤,就提前觸發(fā)垃圾收集以獲取足夠的連續(xù)空間來分給他們养盗。

虛擬機(jī)提供-XX:PretenureSizeThreshold參數(shù),令大于這個(gè)設(shè)置值的對(duì)象直接進(jìn)入老年代适篙,這么做為目的是為了避免在Eden以及兩個(gè)Survivor區(qū)之間發(fā)生大量的內(nèi)存復(fù)制(新生代的垃圾收集算法采用復(fù)制算法)往核。

長(zhǎng)期存活的對(duì)象將進(jìn)入老年代

虛擬機(jī)給每個(gè)對(duì)象定義一個(gè)對(duì)象年齡(Age)的計(jì)數(shù)器,如果對(duì)象在Eden出生并經(jīng)過第一次Minor GC后仍然存活嚷节,并且能被Survivor容納的話聂儒,將被移動(dòng)到Survivor空間中,并且對(duì)象年齡設(shè)為1.其在Survivor中沒經(jīng)歷一次Minior GC硫痰,Age就加1衩婚,當(dāng)其Age增加到一定程度(默認(rèn)15歲),就將其晉升到老年代效斑。年齡閾值可以通過參數(shù)-XX:MxTenuringThreshold設(shè)置谅猾。

動(dòng)態(tài)對(duì)象的年齡判定

為了能更好的適應(yīng)不同程序的內(nèi)存狀況,虛擬機(jī)不是永遠(yuǎn)地要求對(duì)象的年齡必須達(dá)到MaxTenuringThreshold才能晉升老年代,如果Survivor空間中相同年齡所有對(duì)象大小的總和大于Survivor空間的一半税娜,年齡大于或者等于該年齡的對(duì)象就可以直接進(jìn)入老年代坐搔。

空間分配擔(dān)保

在發(fā)生Minor GC之前,虛擬機(jī)會(huì)先檢查老年代中最大可用連續(xù)空間是否大于新生代所有對(duì)象總空間敬矩,如果這個(gè)條件成立概行,那么Minor GC可以確保是安全的。如果不成立弧岳,則虛擬機(jī)會(huì)查看HandlePromotionFailure設(shè)置值是否允許擔(dān)保失敗凳忙。如果允許,那么會(huì)繼續(xù)檢查老年代最大可用的連續(xù)空間是否大于歷次晉升到老年代對(duì)象的平均大小禽炬,如果大于將嘗試進(jìn)行一次Minor GC涧卵。如果小于或者HandlePromotionFailure設(shè)置為不允許,那這時(shí)就改為一次Full GC腹尖。

分配擔(dān)保解釋:新生代使用復(fù)制算法完成垃圾收集柳恐,為了節(jié)約內(nèi)存Survivor的設(shè)置的比較小,當(dāng)Minor GC后如果還有大量對(duì)象存活热幔,超過了一個(gè)Survivor的內(nèi)存空間乐设,這時(shí)就需要老年代進(jìn)行分配擔(dān)保,把Survivor中無法容納的對(duì)象直接進(jìn)入老年代绎巨。若虛擬機(jī)檢查老年代中最大可用連續(xù)空間大于新生代所有對(duì)象總空間那么就能保證不需要發(fā)生Full GC近尚,因?yàn)槔夏甏膬?nèi)存空間夠用。反之场勤,如果老年代中最大可用連續(xù)空間小于新生代所有對(duì)象總空間就需要在嘗試Minor GC失敗后進(jìn)行Full Gc或者直接Full GC戈锻。

談?wù)?種gc算法

1:標(biāo)記—清除 Mark-Sweep

過程:標(biāo)記可回收對(duì)象,進(jìn)行清除

缺點(diǎn):標(biāo)記和清除效率低和媳,清除后會(huì)產(chǎn)生內(nèi)存碎片

2:復(fù)制算法

過程:將內(nèi)存劃分為相等的兩塊舶沛,將存活的對(duì)象復(fù)制到另一塊內(nèi)存,把已經(jīng)使用的內(nèi)存清理掉

缺點(diǎn):使用的內(nèi)存變?yōu)榱嗽瓉淼囊话?/p>

進(jìn)化:將一塊內(nèi)存按8:1的比例分為一塊Eden區(qū)(80%)和兩塊Survivor區(qū)(10%)

每次使用Eden和一塊Survivor窗价,回收時(shí)如庭,將存活的對(duì)象一次性復(fù)制到另一塊Survivor上,如果另一塊Survivor空間不足撼港,則使用分配擔(dān)保機(jī)制存入老年代

3:標(biāo)記—整理 Mark—Compact

過程:所有存活的對(duì)象向一端移動(dòng)坪它,然后清除掉邊界以外的內(nèi)存

4:分代收集算法

過程:將堆分為新生代和老年代,根據(jù)區(qū)域特點(diǎn)選用不同的收集算法帝牡,如果新生代朝生夕死往毡,則采用復(fù)制算法,老年代采用標(biāo)記清除靶溜,或標(biāo)記整理

談?wù)凧ava的垃圾回收機(jī)制以及觸發(fā)時(shí)機(jī)

內(nèi)存回收機(jī)制:就是釋放掉在內(nèi)存中已經(jīng)沒有用的對(duì)象开瞭,要判斷怎樣的對(duì)象是沒用的懒震,有兩種方法:(1)采用標(biāo)記數(shù)的方法,在給內(nèi)存中的對(duì)象打上標(biāo)記嗤详,對(duì)象被引用一次个扰,計(jì)數(shù)加一,引用被釋放葱色,計(jì)數(shù)就減一递宅,當(dāng)這個(gè)計(jì)數(shù)為零時(shí),這個(gè)對(duì)象就可以被回收苍狰,但是办龄,此種方法,對(duì)于循環(huán)引用的對(duì)象是無法識(shí)別出來并加以回收的淋昭,(2)采用根搜索的方法俐填,從一個(gè)根出發(fā),搜索所有的可達(dá)對(duì)象翔忽,則剩下的對(duì)象就是可被回收的英融,垃圾回收是在虛擬機(jī)空閑的時(shí)候或者內(nèi)存緊張的時(shí)候執(zhí)行的,什么時(shí)候回收并不是由程序員控制的呀打,可達(dá)與不可達(dá)的概念:分配對(duì)象使用new關(guān)鍵字,釋放對(duì)象時(shí)糯笙,只需將對(duì)象的引用賦值為null贬丛,讓程序不能夠在訪問到這個(gè)對(duì)象,則稱該對(duì)象不可達(dá)给涕。

在以下情況中垃圾回收機(jī)制會(huì)被觸發(fā):

(1)所有實(shí)例都沒有活動(dòng)線程訪問 豺憔;(2)沒有其他任何實(shí)例訪問的循環(huán)引用實(shí)例;(3)Java中有不同的引用類型够庙。判斷實(shí)例是否符合垃圾收集的條件都依賴于它的引用類型恭应。

From safier

垃圾收集算法

1. Mark-Sweep(標(biāo)記-清除)算法

這是最基礎(chǔ)的垃圾回收算法,之所以說它是最基礎(chǔ)的是因?yàn)樗钊菀讓?shí)現(xiàn)耘眨,思想也是最簡(jiǎn)單的昼榛。標(biāo)記-清除算法分為兩個(gè)階段:標(biāo)記階段和清除階段。標(biāo)記階段的任務(wù)是標(biāo)記出所有需要被回收的對(duì)象剔难,清除階段就是回收被標(biāo)記的對(duì)象所占用的空間胆屿。具體過程如下圖所示:

從圖中可以很容易看出標(biāo)記-清除算法實(shí)現(xiàn)起來比較容易,但是有一個(gè)比較嚴(yán)重的問題就是容易產(chǎn)生內(nèi)存碎片偶宫,碎片太多可能會(huì)導(dǎo)致后續(xù)過程中需要為大對(duì)象分配空間時(shí)無法找到足夠的空間而提前觸發(fā)新的一次垃圾收集動(dòng)作非迹。

2. Copying(復(fù)制)算法

為了解決Mark-Sweep算法的缺陷,Copying算法就被提了出來纯趋。它將可用內(nèi)存按容量劃分為大小相等的兩塊憎兽,每次只使用其中的一塊冷离。當(dāng)這一塊的內(nèi)存用完了,就將還存活著的對(duì)象復(fù)制到另外一塊上面纯命,然后再把已使用的內(nèi)存空間一次清理掉西剥,這樣一來就不容易出現(xiàn)內(nèi)存碎片的問題。具體過程如下圖所示:

這種算法雖然實(shí)現(xiàn)簡(jiǎn)單扎附,運(yùn)行高效且不容易產(chǎn)生內(nèi)存碎片蔫耽,但是卻對(duì)內(nèi)存空間的使用做出了高昂的代價(jià),因?yàn)槟軌蚴褂玫膬?nèi)存縮減到原來的一半留夜。

3. Mark-Compact(標(biāo)記-整理)算法

了解決Copying算法的缺陷匙铡,充分利用內(nèi)存空間,提出了Mark-Compact算法碍粥。該算法標(biāo)記階段和Mark-Sweep一樣鳖眼,但是在完成標(biāo)記之后,它不是直接清理可回收對(duì)象嚼摩,而是將存活對(duì)象都向一端移動(dòng)钦讳,然后清理掉端邊界以外的內(nèi)存。具體過程如下圖所示:

4. Generational Collection(分代收集)算法

分代收集算法是目前大部分JVM的垃圾收集器采用的算法枕面。它的核心思想是根據(jù)對(duì)象存活的生命周期將內(nèi)存劃分為若干個(gè)不同的區(qū)域愿卒。一般情況下將堆區(qū)劃分為老年代(Tenured Generation)和新生代(Young Generation),老年代的特點(diǎn)是每次垃圾收集時(shí)只有少量對(duì)象需要被回收潮秘,而新生代的特點(diǎn)是每次垃圾回收時(shí)都有大量的對(duì)象需要被回收琼开,那么就可以根據(jù)不同代的特點(diǎn)采取最適合的收集算法。

目前大部分垃圾收集器對(duì)于新生代都采取Copying算法枕荞,因?yàn)樾律忻看卫厥斩家厥沾蟛糠謱?duì)象柜候,也就是說需要復(fù)制的操作次數(shù)較少,但是實(shí)際中并不是按照1:1的比例來劃分新生代的空間的躏精,一般來說是將新生代劃分為一塊較大的Eden空間和兩塊較小的Survivor空間渣刷,每次使用Eden空間和其中的一塊Survivor空間,當(dāng)進(jìn)行回收時(shí)矗烛,將Eden和Survivor中還存活的對(duì)象復(fù)制到另一塊Survivor空間中辅柴,然后清理掉Eden和剛才使用過的Survivor空間。

而由于老年代的特點(diǎn)是每次回收都只回收少量對(duì)象瞭吃,一般使用的是Mark-Compact算法碌识。

注意,在堆區(qū)之外還有一個(gè)代就是永久代(Permanet Generation)虱而,它用來存儲(chǔ)class類筏餐、常量、方法描述等牡拇。對(duì)永久代的回收主要回收兩部分內(nèi)容:廢棄常量和無用的類魁瞪。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末穆律,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子导俘,更是在濱河造成了極大的恐慌峦耘,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,807評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件旅薄,死亡現(xiàn)場(chǎng)離奇詭異辅髓,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)少梁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門洛口,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人凯沪,你說我怎么就攤上這事第焰。” “怎么了妨马?”我有些...
    開封第一講書人閱讀 169,589評(píng)論 0 363
  • 文/不壞的土叔 我叫張陵挺举,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我烘跺,道長(zhǎng)湘纵,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,188評(píng)論 1 300
  • 正文 為了忘掉前任滤淳,我火速辦了婚禮梧喷,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘娇钱。我一直安慰自己伤柄,他們只是感情好绊困,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,185評(píng)論 6 398
  • 文/花漫 我一把揭開白布文搂。 她就那樣靜靜地躺著,像睡著了一般秤朗。 火紅的嫁衣襯著肌膚如雪煤蹭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,785評(píng)論 1 314
  • 那天取视,我揣著相機(jī)與錄音硝皂,去河邊找鬼。 笑死作谭,一個(gè)胖子當(dāng)著我的面吹牛稽物,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播折欠,決...
    沈念sama閱讀 41,220評(píng)論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼贝或,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼吼过!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起咪奖,我...
    開封第一講書人閱讀 40,167評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤盗忱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后羊赵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體趟佃,經(jīng)...
    沈念sama閱讀 46,698評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,767評(píng)論 3 343
  • 正文 我和宋清朗相戀三年昧捷,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了闲昭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,912評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡料身,死狀恐怖汤纸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情芹血,我是刑警寧澤贮泞,帶...
    沈念sama閱讀 36,572評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站幔烛,受9級(jí)特大地震影響啃擦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜饿悬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,254評(píng)論 3 336
  • 文/蒙蒙 一令蛉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧狡恬,春花似錦珠叔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至兔乞,卻和暖如春汇鞭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背庸追。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工霍骄, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人淡溯。 一個(gè)月前我還...
    沈念sama閱讀 49,359評(píng)論 3 379
  • 正文 我出身青樓读整,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親咱娶。 傳聞我的和親對(duì)象是個(gè)殘疾皇子米间,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,922評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • 所有知識(shí)點(diǎn)已整理成app app下載地址 J2EE 部分: 1.Switch能否用string做參數(shù)煎楣? 在 Jav...
    侯蛋蛋_閱讀 2,453評(píng)論 1 4
  • 一:java概述:1,JDK:Java Development Kit车伞,java的開發(fā)和運(yùn)行環(huán)境择懂,java的開發(fā)工...
    ZaneInTheSun閱讀 2,662評(píng)論 0 11
  • 第6章類文件結(jié)構(gòu) 6.1 概述 6.2 無關(guān)性基石 6.3 Class類文件的結(jié)構(gòu) java虛擬機(jī)不和包括java...
    kennethan閱讀 940評(píng)論 0 2
  • 1.面向?qū)ο蟮奶卣饔心男┓矫妫?抽象:抽象是將一類對(duì)象的共同特征總結(jié)出來構(gòu)造類的過程,包括數(shù)據(jù)抽象和行為抽象兩方面...
    浪花易逝閱讀 655評(píng)論 0 5
  • 第二部分 自動(dòng)內(nèi)存管理機(jī)制 第二章 java內(nèi)存異常與內(nèi)存溢出異常 運(yùn)行數(shù)據(jù)區(qū)域 程序計(jì)數(shù)器:當(dāng)前線程所執(zhí)行的字節(jié)...
    小明oh閱讀 1,174評(píng)論 0 2