Java小結(jié)

一、線程部分

1、Java 中引用類(lèi)型都有哪些?

(1)強(qiáng)引用。在虛擬機(jī)內(nèi)存不足的情況下,也不會(huì)回收,如果我們把(強(qiáng)引用)對(duì)象置為null,會(huì)大大增加了垃圾回收的頻率。幾乎只要我們給出建議(GC),JVM就會(huì)回收。

Object o = new Object();
Object o1 = o;

(2) 軟引用。如果不顯式的置為null,跟強(qiáng)引用差不多。垃圾回收不會(huì)執(zhí)行。只會(huì)等到內(nèi)存不足的時(shí)候才會(huì)調(diào)用。

(3)弱引用。就算不置為null,發(fā)生垃圾回收時(shí),會(huì)立即被回收。

(4)虛引用。相當(dāng)于null

2、談一談java 線程模型

線程的實(shí)現(xiàn)可以分為兩類(lèi):用戶級(jí)線程(User-LevelThread, ULT)和內(nèi)核級(jí)線程(Kemel-LevelThread, KLT)。用戶線程由用戶代碼支持,內(nèi)核線程由操作系統(tǒng)內(nèi)核支持。

(1)一對(duì)一模型

每個(gè)用戶線程對(duì)應(yīng)一個(gè)內(nèi)核線程。由于每個(gè)用戶線程對(duì)應(yīng)自己的內(nèi)核線程,所以他們互不影響,當(dāng)一個(gè)線程阻塞,也允許另外的線程繼續(xù)執(zhí)行,這是此模型的優(yōu)點(diǎn)。但也存在一個(gè)缺陷,由于一對(duì)一的關(guān)系,有多少用戶線程就代表有多少內(nèi)核線程,由于內(nèi)核線程的開(kāi)銷(xiāo)比較大,一般操作系統(tǒng)會(huì)都有內(nèi)核線程數(shù)量的限制,所以也限制了用戶線程的數(shù)量。

(2)多對(duì)一模型

一個(gè)內(nèi)核線程實(shí)現(xiàn)若干個(gè)用戶線程的并發(fā)功能,線程的管理在用戶空間中進(jìn)行,一般不需要切換到內(nèi)核態(tài),效率較高,而且比起一對(duì)一模型,支持的用戶線程數(shù)量更多。但此模型有個(gè)致命的弱點(diǎn)是如果一個(gè)線程執(zhí)行了阻塞調(diào)用,所有的線程都將阻塞,并且任意時(shí)刻都只能有一個(gè)線程訪問(wèn)內(nèi)核。另外,對(duì)線程的所有操作,都將由用戶自己處理。一般除了不支持多線程操作的系統(tǒng)被迫使用此模型外,在多線程操作系統(tǒng)中不會(huì)使用該模型。

(3)多對(duì)多模型

多對(duì)多模型的提出是為了解決以上兩種模型的缺點(diǎn),多個(gè)用戶線程與多個(gè)內(nèi)核線程映射形成了多路復(fù)用。前面一對(duì)一模型存在受內(nèi)核線程數(shù)量限制的問(wèn)題,多對(duì)一模型解決了這個(gè)問(wèn)題,但它存在一個(gè)線程阻塞所有線程都阻塞的風(fēng)險(xiǎn),而且一個(gè)內(nèi)核線程只能調(diào)用一個(gè)用戶線程導(dǎo)致并發(fā)性不強(qiáng)。看看多對(duì)多模型如何解決這些問(wèn)題,由于多對(duì)一是多對(duì)多的子集,所以多對(duì)多具備多對(duì)一的優(yōu)點(diǎn),線程數(shù)不受限制。除此之外,多個(gè)內(nèi)核線程可處理多個(gè)用戶線程,當(dāng)某個(gè)線程阻塞時(shí),將可以調(diào)度另外一個(gè)線程執(zhí)行,這從另一方面看也是增強(qiáng)了并發(fā)性。

二、JVM

1、JVM內(nèi)存結(jié)構(gòu)說(shuō)一下

(一)運(yùn)行時(shí)數(shù)據(jù)區(qū):JVM在執(zhí)行Java程序過(guò)程中,把所管理的內(nèi)存劃分為不同的區(qū)域。有

(1)程序計(jì)數(shù)器。

記錄當(dāng)前線程正在執(zhí)行字節(jié)碼指令的地址,因?yàn)镃PU的時(shí)間片輪轉(zhuǎn)機(jī)制,當(dāng)CPU切換到其他線程執(zhí)行后再切回當(dāng)前線程根據(jù)程序計(jì)數(shù)器的記錄繼續(xù)執(zhí)行。它是線程私有的,也是唯一一塊不會(huì)發(fā)生OOM的區(qū)域。

(2)虛擬機(jī)棧

棧的特點(diǎn)是先進(jìn)后出(FILO),存儲(chǔ)當(dāng)前線程運(yùn)行方法所需的數(shù)據(jù)、指令、地址。它是線程私有的。里邊有若干棧幀(每個(gè)java方法對(duì)應(yīng)一個(gè)棧幀),棧幀有局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接、完成出口(返回地址)組成。局部變量表中存放8大基本數(shù)據(jù)類(lèi)型和對(duì)象的引用;操作數(shù)棧存放方法的執(zhí)行和操作;動(dòng)態(tài)鏈接:是由于多態(tài),靜態(tài)分派、動(dòng)態(tài)分派;
完成出口(返回地址):
    把符號(hào)引用轉(zhuǎn)換成具體引用
    正常返回,會(huì)調(diào)用程序計(jì)數(shù)器中的地址進(jìn)行返回;
    異常返回,會(huì)根據(jù)異常處理表來(lái)確定。

在Java中的解析執(zhí)行是基于棧的,基于棧幀的執(zhí)行主要說(shuō)的是操作數(shù)棧。c語(yǔ)言是基于寄存器的。

Java基于棧:兼容性好,效率低;基于寄存器:寄存器是硬件,所以快一點(diǎn),但移植性差。

可以再講一下方法執(zhí)行的流程。

(3)本地方法棧

保存的是native方法的信息。它是線程私有的。

當(dāng) JVM 創(chuàng)建的線程調(diào)用native方法時(shí),JVM不會(huì)在虛擬機(jī)棧上創(chuàng)建棧幀,只是簡(jiǎn)單的動(dòng)態(tài)鏈接并直接調(diào)用native方法。程序計(jì)數(shù)器也不會(huì)記錄。

(4)方法區(qū)

存放著class類(lèi)信息、靜態(tài)變量、常量、即時(shí)編譯期編譯后的代碼。線程間共享的。

實(shí)現(xiàn)方式 JDK 1.8 以后是元空間,JDK1.7是永久代。元空間可以使用硬件內(nèi)存(堆以外的內(nèi)存),方便擴(kuò)展;永久代是受制于堆內(nèi)存。

(5)堆

存放著幾乎所有的對(duì)象、數(shù)組。線程間共享的。

方法區(qū)和堆為什么劃分為兩個(gè)區(qū)?

堆中存放著幾乎所有的對(duì)象、數(shù)組,這些是頻繁回收的;方法區(qū)中存放的類(lèi)信息、常量、靜態(tài)變量、即時(shí)編譯期編譯后的代碼回收的難度是相當(dāng)大的。動(dòng)靜分離的思想,便于垃圾回收的高效。

(二)直接內(nèi)存(堆外內(nèi)存)

不是JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)的一部分,也不是Java虛擬機(jī)規(guī)范中定義的內(nèi)存區(qū)域。

如果使用NIO會(huì)頻繁的使用該區(qū)域,在Java堆內(nèi)使用 DirectByteBuffer 對(duì)象可以直接引用并操作。

不受堆內(nèi)存大小的限制,但受本機(jī)內(nèi)存大小的限制??梢酝ㄟ^(guò)MaxDirectMemorySize來(lái)設(shè)置,默認(rèn)與堆內(nèi)存最大值一樣,所以也會(huì)出現(xiàn)OOM異常。

2、什么情況下內(nèi)存棧溢出?

虛擬機(jī)棧是有深度限制的,如果加載的方法過(guò)多會(huì)發(fā)生 stackoverflowerror 造成溢出;棧內(nèi)存空間不足也會(huì)發(fā)生OOM。

3、描述一下new 一個(gè)對(duì)象的過(guò)程?

首先,<u>檢查加載</u>,若類(lèi)信息沒(méi)有加載進(jìn)方法區(qū),進(jìn)行 <u>類(lèi)加載</u>。若已加載,進(jìn)行<u>分配內(nèi)存</u>,如果內(nèi)存是規(guī)整的,使用 指針碰撞 進(jìn)行內(nèi)存分配,若內(nèi)存不規(guī)整,使用 空閑列表 進(jìn)行內(nèi)存分配。同時(shí)要注意并發(fā)安全問(wèn)題,CAS加失敗重試、本地線程分配緩存(TLAB)。本地線程分配緩存(TLAB)是線程的一塊私有內(nèi)存,在堆的Eden區(qū)中開(kāi)辟一塊區(qū)域來(lái)存放對(duì)象。對(duì)象占據(jù)的內(nèi)存一定是連續(xù)的。之后是<u>內(nèi)存空間初始化</u>,也就是把數(shù)據(jù)初始值置為零值,比如int類(lèi)型的 a = 0; boolean類(lèi)型的 b = false。然后進(jìn)行<u>設(shè)置</u>,設(shè)置對(duì)象頭等信息。最后是<u>對(duì)象初始化</u>,調(diào)用對(duì)象的構(gòu)造函數(shù)等。

TLAB是線程的一塊私有內(nèi)存,它是虛擬機(jī)在堆內(nèi)存的eden劃分出來(lái)的,如果設(shè)置了虛擬機(jī)參數(shù) -XX:UseTLAB,在線程初始化時(shí),同時(shí)也會(huì)申請(qǐng)一塊指定大小的內(nèi)存, 只給當(dāng)前線程使用, 這樣每個(gè)線程都單獨(dú)擁有一個(gè)Buffer,如果需要分配內(nèi)存,就在自己的Buffer上分配,這樣就不存在競(jìng)爭(zhēng)的情況,可以大大提升分配效率,當(dāng)Buffer容量不夠的時(shí)候,再重新從Eden區(qū)域申請(qǐng)一塊繼續(xù)使用。

4、Java對(duì)象會(huì)不會(huì)分配在棧中?

會(huì)分配在棧中。滿足逃逸分析的對(duì)象會(huì)直接分配在棧中。

逃逸分析:方法逃逸、線程逃逸

對(duì)象分配的原則:

  • 對(duì)象優(yōu)先在Eden區(qū)分配
  • 空間分配擔(dān)保
  • 大對(duì)象直接進(jìn)入老年代
  • 長(zhǎng)期存活的對(duì)象進(jìn)入老年代
  • 動(dòng)態(tài)判斷對(duì)象的年齡

堆中的優(yōu)化技術(shù): 本地線程分配緩沖(TLAB)是線程的一塊私有內(nèi)存.

new出一個(gè)對(duì)象,首先判斷是否滿足棧中分配的條件(逃逸分析),若滿足直接在棧中分配;若不滿足,繼續(xù)判斷是否可以使用 本地線程分配緩沖,若可以在堆的Eden區(qū)單獨(dú)開(kāi)辟出TLAB區(qū)存放,則存放在TLAB區(qū),若不行,則繼續(xù)判斷是否是大對(duì)象,如果是大對(duì)象,直接在老年代分配內(nèi)存,若不是,分配在Eden區(qū)。

5、判斷一個(gè)對(duì)象是否被回收,有些算法,實(shí)際虛擬機(jī)使用的最多的是什么?

(1)引用計(jì)數(shù)法

python中使用的是這種。有引用,計(jì)數(shù)+1,不在引用,計(jì)數(shù) -1,當(dāng)計(jì)數(shù)=0,就回收。存在一個(gè)問(wèn)題,對(duì)象間的相互引用問(wèn)題,需要額外的補(bǔ)償機(jī)制回收相互引用的對(duì)象。

(2)可達(dá)性分析算法(根可達(dá))--- 實(shí)際虛擬機(jī)使用的最多

可以成為 GC Roots 的對(duì)象有:靜態(tài)變量、線程棧變量(虛擬機(jī)棧中的局部變量中的變量)、常量池、JNI指針,還有class、異常NullPointException等、類(lèi)加載器、加鎖的synchronized對(duì)象、jmxBean、臨時(shí)性的等。

6、GC算法有哪些?它們的特點(diǎn)是?

(1)復(fù)制算法,把使用的對(duì)象從一塊空間復(fù)制到另一塊空間。特點(diǎn):實(shí)現(xiàn)簡(jiǎn)單、運(yùn)行高效;內(nèi)存復(fù)制,沒(méi)有內(nèi)存碎片;空間利用率只有一半。

(2)標(biāo)記清除算法。特點(diǎn):執(zhí)行效率不穩(wěn)定,因?yàn)樾枰厥盏膶?duì)象多少不確定;內(nèi)存碎片會(huì)導(dǎo)致提前GC。

(3)標(biāo)記整理算法。特點(diǎn):對(duì)象移動(dòng);引用更新;用戶線程暫停;沒(méi)有內(nèi)存碎片。

7、JVM中一次完成的GC流程是怎樣的?對(duì)象如何晉級(jí)老年代?

8、final、finally、finalize的區(qū)別?

final:

修飾類(lèi),表示類(lèi)不可擴(kuò)展。

修飾方法,一是不可修改,二是效率。

修飾變量,只能賦值一次。

finally:

try catch finally  不管有沒(méi)有異常都會(huì)執(zhí)行,釋放資源

finalize:
Object中的方法
對(duì)象垃圾回收可以拯救一下對(duì)象,不可靠,一般不用

9、String s = new String("abc"); 創(chuàng)建了幾個(gè)對(duì)象?

2個(gè)。

首先,在編譯文件時(shí),"abc"常量字符串會(huì)加入到常量結(jié)構(gòu)中,在類(lèi)加載時(shí),"abc"會(huì)在常量池中創(chuàng)建;

其次,調(diào)用new 時(shí), jvm 命令將調(diào)用String的構(gòu)造函數(shù),同時(shí)引用常量池中的"abc"字符串,在堆中創(chuàng)建一個(gè)對(duì)象;

最后,s引用這個(gè)對(duì)象。

常量池(方法區(qū))分為靜態(tài)常量池、運(yùn)行時(shí)常量池。

靜態(tài)常量池中,存放字面量、符號(hào)引用、類(lèi)信息、方法的信息等

運(yùn)行時(shí)常量池中,存放 類(lèi)加載,加載到運(yùn)行時(shí)數(shù)據(jù)區(qū)的方法區(qū)、轉(zhuǎn)換成實(shí)體類(lèi),把符號(hào)引用變成直接引用(hash值)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容