對(duì)象一定分配在堆中嗎?有沒(méi)有了解逃逸分析技術(shù)?
- 不一定的,JVM 通過(guò)「逃逸分析」 ,那些逃不出方法的對(duì)象會(huì)在棧上分配.
逃逸分析是指分析指針動(dòng)態(tài)范圍的方法,它同編譯器優(yōu)化原理的指針?lè)治龊屯庑畏治鱿嚓P(guān)聯(lián)。當(dāng)
變 量(或者對(duì)象)在方法中分配后,其指針有可能被返回或者被全局引用,這樣就會(huì)被其他方法
或者 線(xiàn)程所引用,這種現(xiàn)象稱(chēng)作指針(或者引用)的逃逸(Escape)。通俗點(diǎn)講,如果一個(gè)對(duì)象
的指針被 多個(gè)方法或者線(xiàn)程引用時(shí),那么我們就稱(chēng)這個(gè)對(duì)象的指針發(fā)生了逃逸。
volatile:可見(jiàn)性、順序性
保證并發(fā)編程的三要素(可見(jiàn)性、順序性、原子性)之二,可結(jié)合AtomicInteger等對(duì)象使用
volatile 本質(zhì)是在告訴 jvm 當(dāng)前變量在寄存器(工作內(nèi)存)中的值是不確定的,需要從主存中讀 取
使用 volatile 一般用于 狀態(tài)標(biāo)記量 和 單例模式的雙檢鎖。
1)保證了不同線(xiàn)程對(duì)這個(gè)變量進(jìn)行操作時(shí)的可見(jiàn)性,即一個(gè)線(xiàn)程修改了某個(gè)變量的值,這新值對(duì) 其他線(xiàn)程來(lái)說(shuō)是立即可見(jiàn)的,volatile 關(guān)鍵字會(huì)強(qiáng)制將修改的值立即寫(xiě)入主存。
2)禁止進(jìn)行指令重排序。
x = 2;//語(yǔ)句 1
y = 0;//語(yǔ)句 2
flag = true;//語(yǔ)句 3
x = 4;//語(yǔ)句 4
y = -1;//語(yǔ)句 5
由于flag 變量為 volatile 變量,那么在進(jìn)行指令重排序的過(guò)程的時(shí)候,不會(huì)將語(yǔ)句 3 放到語(yǔ)句 1、
語(yǔ)句 2 前面,也不會(huì)將語(yǔ)句 3 放到語(yǔ)句 4、語(yǔ)句 5 后面。但是要注意語(yǔ)句 1 和語(yǔ)句 2 的順序、語(yǔ)
句 4 和語(yǔ)句 5 的順序是不做任何保證的。
Thread中start()和run()區(qū)別
start()方法被用來(lái)啟動(dòng)新創(chuàng)建的線(xiàn)程,而且 start()內(nèi)部調(diào)用了 run()方法,這和直接調(diào)用 run()方法的 效果不一樣。當(dāng)你調(diào)用 run()方法的時(shí)候,只會(huì)是在原來(lái)的線(xiàn)程中調(diào)用,沒(méi)有新的線(xiàn)程啟動(dòng),start() 方法才會(huì)啟動(dòng)新線(xiàn)程。
Java對(duì)象創(chuàng)建過(guò)程
- 類(lèi)加載器將類(lèi)加載到內(nèi)存里
- 對(duì)類(lèi)的靜態(tài)變量、成員變量、靜態(tài)代碼塊進(jìn)行初始化
- 根據(jù)類(lèi)信息得知目標(biāo)對(duì)象大小,在堆內(nèi)存里分配空間
- 內(nèi)存分配的方式一般有兩種,一種指針碰撞,另一種是空閑列表,JVM 會(huì)根據(jù) Java 堆內(nèi)存是否規(guī)整來(lái)決定內(nèi)存分配方式
- 目標(biāo)對(duì)象里的普通成員變量初始化為零值,int為0,對(duì)象為null
- 設(shè)置對(duì)象頭:如類(lèi)元信息、GC分代年齡、hashcode、鎖標(biāo)記等
- 執(zhí)行目標(biāo)對(duì)象內(nèi)部生成的 init 方法
- init 方法是 Java 文件編譯之后在字節(jié)碼文件中生成的,它是一個(gè)實(shí)例構(gòu)造器,這個(gè)構(gòu)造器會(huì)把語(yǔ)句塊、變量初始化、調(diào)用父類(lèi)構(gòu)造器等操作組織在一起。
為什么ConcurrentHashMap的key、value不允許為null,而HashMap允許?
- 防止歧義:get(key)的時(shí)候返回null,不確定是key不存在,還是value=null
- HashMap可以使用containsKey(key)來(lái)判斷key是否存在,ConcurrentHashMap使用containsKey()后再使用get(key)不具有原子性,無(wú)法做出判斷
ThreadLocal會(huì)出現(xiàn)內(nèi)存泄漏嗎?
- 回答:在使用不當(dāng)時(shí)會(huì)出現(xiàn)內(nèi)存泄露
Thread的成員變量ThreadLocalMap的key是指向ThreadLocal對(duì)象的弱引用,代碼如下:
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
若ThreadLocal對(duì)象無(wú)其它強(qiáng)引用,那么可能會(huì)被GC,導(dǎo)致ThreadLocalMap的key為null,其對(duì)應(yīng)的value就無(wú)法被訪(fǎng)問(wèn),出現(xiàn)內(nèi)存泄露
- 解決辦法:每次使用完 ThreadLocal 以后,主動(dòng)調(diào)用 remove()方法移除數(shù)據(jù)
創(chuàng)建線(xiàn)程池的辦法
- 使用Executors
- newFixedThreadPool(int nThreads):創(chuàng)建一個(gè)固定大小的線(xiàn)程池。
- newCachedThreadPool():創(chuàng)建一個(gè)可根據(jù)需要?jiǎng)?chuàng)建新線(xiàn)程的線(xiàn)程池。
- newScheduledThreadPool(int corePoolSize):創(chuàng)建一個(gè)固定大小的線(xiàn)程池,可以執(zhí)行定時(shí)任務(wù)。
- newSingleThreadExecutor():創(chuàng)建一個(gè)單線(xiàn)程的線(xiàn)程池。
- 使用 ThreadPoolExecutor 類(lèi)的構(gòu)造方法創(chuàng)建自定義線(xiàn)程池