數(shù)組實(shí)現(xiàn)隊(duì)列
public class ArrayQueue {
private String [] items; //定義數(shù)組
private int n = 0; //數(shù)組大小
private int head = 0; //表示隊(duì)頭下標(biāo)
private int tail = 0; //表示隊(duì)尾下標(biāo)
//申請(qǐng)一個(gè)大小為capacity的數(shù)組
public ArrayQueue(int capacity) {
this.n = capacity;
this.items = new String[capacity]; //初始化數(shù)組
}
public boolean enQueue(String item) {
if (tail == n) { //隊(duì)列已經(jīng)滿了
return false;
}
items[tail] = item;
tail++;
return true;
}
public String deQueue() {
if (head == tail) { //隊(duì)列為空
return null;
}
String item = items[head];
head++;
return item;
}
}
public class ArrayStack {
String[] arrays;
int i = 0;
public ArrayStack(int n) {
arrays = new String[n];
}
public void push(String item) {
if (i > arrays.length - 1) {
return;
}
arrays[i] = item;
i++;
}
public String pop() {
if (i > 0) {
i--;
}
String temp = arrays[i];
arrays[i] = null;
return temp;
}
}
java軟引用與弱引用區(qū)別
參考了一些資料
- 強(qiáng)引用
- 我們平常典型編碼Object obj = new Object()中的obj就是強(qiáng)引用宾毒。通過關(guān)鍵字new創(chuàng)建的對(duì)象所關(guān)聯(lián)的引用就是強(qiáng)引用熏兄。
- 當(dāng)JVM內(nèi)存空間不足挺庞,JVM寧愿拋出OutOfMemoryError運(yùn)行時(shí)錯(cuò)誤(OOM),使程序異常終止辩恼,也不會(huì)靠隨意回收具有強(qiáng)引用的“存活”對(duì)象來解決內(nèi)存不足的問題。
- 對(duì)于一個(gè)普通的對(duì)象瞧甩,如果沒有其他的引用關(guān)系钉跷,只要超過了引用的作用域或者顯式地將相應(yīng)強(qiáng)引用賦值為 null,就是可以被垃圾收集的了肚逸,具體回收時(shí)機(jī)還是要看垃圾收集策略爷辙。
- 軟引用(SoftReference)
- 只有當(dāng) JVM 認(rèn)為內(nèi)存不足時(shí),才會(huì)去試圖回收軟引用指向的對(duì)象朦促,即JVM 會(huì)確保在拋出 OutOfMemoryError 之前膝晾,清理軟引用指向的對(duì)象。
- 當(dāng)JVM認(rèn)為內(nèi)存充足的時(shí)候务冕,不會(huì)去回收軟引用
- 軟引用什么時(shí)候會(huì)被回收
- 弱引用(WeakReference)
- 當(dāng)發(fā)生GC時(shí)血当,如果掃描到一個(gè)對(duì)象只有弱引用,不管當(dāng)前內(nèi)存是否足夠禀忆,都會(huì)對(duì)它進(jìn)行回收臊旭。
String str = new String("abc"); //創(chuàng)建一個(gè)弱引用,讓這個(gè)弱引用引用到str字符串 WeakReference weakReference = new WeakReference(str); //切斷str引用和 str 字符串之間的引用箩退,此時(shí)str 只有一個(gè)弱引用weakReference指向它 str = null; // 沒有進(jìn)行垃圾回收离熏,我們還可以通過弱引用來訪問他 System.out.println(weakReference.get()); // abc //強(qiáng)制進(jìn)行垃圾回收 System.gc(); //再次取出弱引用所引用的對(duì)象 System.out.println(weakReference.get()); // null
- 虛引用(PhantomReference)
- 垃圾回收時(shí)回收,無法通過引用取到對(duì)象值
- 虛引用是每次垃圾回收的時(shí)候都會(huì)被回收滋戳,通過虛引用的get方法永遠(yuǎn)獲取到的數(shù)據(jù)為null,因此也被成為幽靈引用啥刻。
- 虛引用主要用于檢測(cè)對(duì)象是否已經(jīng)從內(nèi)存中刪除奸鸯。程序可以通過檢查與虛引用關(guān)聯(lián)的引用隊(duì)列中是否已經(jīng)包含了該虛引用,從而了解虛引用所引用對(duì)象是否即將被回收郑什。
- 虛引用與軟引用和弱引用的一個(gè)區(qū)別在于:虛引用必須和引用隊(duì)列 (ReferenceQueue)聯(lián)合使用府喳。當(dāng)垃圾回收器準(zhǔn)備回收一個(gè)對(duì)象時(shí),如果發(fā)現(xiàn)它還有虛引用蘑拯,就會(huì)在回收對(duì)象的內(nèi)存之前钝满,把這個(gè)虛引用加入到與之 關(guān)聯(lián)的引用隊(duì)列中。
final變量用反射修改
-
當(dāng)final修飾的成員變量在定義的時(shí)候就初始化了值申窘,那么java反射機(jī)制就已經(jīng)不能動(dòng)態(tài)修改它的值了弯蚜。
- 原因:編譯期間final類型的數(shù)據(jù)自動(dòng)被優(yōu)化了,即:所有用到該變量的地方都被替換成了常量剃法。
public final String name = "abc"; public String getName() { return "abc"; }
當(dāng)final修飾的成員變量在定義的時(shí)候并沒有初始化值的話碎捺,那么就還能通過java反射機(jī)制來動(dòng)態(tài)修改它的值。
- HashMap的內(nèi)部結(jié)構(gòu),給定一個(gè)key收厨,如何找到對(duì)應(yīng)的value晋柱,使用equal
- volatile
- Java線程池有什么作用
- Java動(dòng)態(tài)代理
- handler機(jī)制
- android跨進(jìn)程通信的方式
- 自定義控件方式
- Canvas繪制過什么 手寫功能
斷點(diǎn)續(xù)傳的實(shí)現(xiàn)
- 從字面上理解,所謂斷點(diǎn)續(xù)傳就是從停止的地方重新下載诵叁。 斷點(diǎn):線程停止的位置雁竞。 續(xù)傳:從停止的位置重新下載。
- 用代碼解析就是:斷點(diǎn): 當(dāng)前線程已經(jīng)下載完成的數(shù)據(jù)長(zhǎng)度拧额。續(xù)傳: 向服務(wù)器請(qǐng)求上次線程停止位置之后的數(shù)據(jù)碑诉。原理知道了,功能實(shí)現(xiàn)起來也簡(jiǎn)單侥锦。每當(dāng)線程停止時(shí)就把已下載的數(shù)據(jù)長(zhǎng)度寫入記錄文件进栽,當(dāng)重新下載時(shí),從記錄文件讀取已經(jīng)下載了的長(zhǎng)度恭垦。而這個(gè)長(zhǎng)度就是所需要的斷點(diǎn)快毛。
- 總結(jié)來說就是下載過程中要使用數(shù)據(jù)庫實(shí)時(shí)存儲(chǔ)到底存儲(chǔ)到文件的哪個(gè)位置了,這樣點(diǎn)擊開始繼續(xù)傳遞時(shí)署照,才能通過HTTP的GET請(qǐng)求中的 urlConnection.setRequestProperty("Range","bytes=" + start + "-" + info.getLength()); 方法可以告訴服務(wù)器祸泪,數(shù)據(jù)從哪里開始,到哪里結(jié)束建芙。同時(shí)在本地的文件寫入時(shí)没隘,RandomAccessFile的seek()方法也支持在文件中的任意位置進(jìn)行寫入操作。同時(shí)通過廣播將子線程的進(jìn)度告訴Activity的ProcessBar禁荸。
app 啟動(dòng)速度的優(yōu)化
影響啟動(dòng)的因素
- 高耗時(shí)任務(wù)
- 復(fù)雜的View層級(jí)
- 類過于復(fù)雜
- 主題及Activity配置
啟動(dòng)耗時(shí)檢測(cè)
- 查看Logcat
- adb shell
- 代碼打點(diǎn)(函數(shù)插樁)
- 啟動(dòng)速度分析工具 — TraceView
- 啟動(dòng)速度分析工具 — Systrace
解決方案
- 異步初始化 : 充分利用CPU多核右蒲,自動(dòng)梳理任務(wù)順序。
- 延遲初始化 :第三方庫懶加載赶熟,按需初始化; 利用IdleHandler特性瑰妄,在CPU空閑時(shí)執(zhí)行,對(duì)延遲任務(wù)進(jìn)行分批初始化
- Multidex預(yù)加載優(yōu)化 啟動(dòng)時(shí)單獨(dú)開一個(gè)進(jìn)程去異步進(jìn)行Multidex的第一次加載映砖,即Dex提取和Dexopt操作间坐。
- 復(fù)雜的View層級(jí) 減少層級(jí)嵌套
- 主題切換:使用Activity的windowBackground主題屬性預(yù)先設(shè)置一個(gè)啟動(dòng)圖片
- 保活
- WebView啟動(dòng)優(yōu)化
線程優(yōu)化
線程調(diào)度模型
- 分時(shí)調(diào)度模型 輪流獲取邑退、均分CPU
- 搶占式調(diào)度模型 優(yōu)先級(jí)高的獲取
如何干預(yù)線程調(diào)度竹宋?
- 設(shè)置線程優(yōu)先級(jí)。
Android異步方式
- Thread 直接創(chuàng)建地技,缺點(diǎn)很多蜈七,比如說不容易被復(fù)用,導(dǎo)致頻繁創(chuàng)建和銷毀線程的開銷大莫矗,不建議使用
- HandlerThread 本質(zhì)上也是一個(gè) Thread飒硅,自帶了消息循環(huán)砂缩,串行執(zhí)行
- IntentService 是 Service 組件的子類,它的內(nèi)部有一個(gè) HandlerThread三娩,所以它具備了 HandlerThread 的特性庵芭。優(yōu)點(diǎn):使用了 Service,會(huì)提高應(yīng)用的優(yōu)先級(jí)雀监;異步喳挑,不占用主線程
- AsyncTask 內(nèi)部實(shí)現(xiàn)使用了線程池
- 線程池 易復(fù)用,減少頻繁創(chuàng)建滔悉、銷毀的時(shí)間;功能強(qiáng)大单绑,如定時(shí)回官、任務(wù)隊(duì)列、并發(fā)數(shù)控制等
- RxJava 功能強(qiáng)大搂橙,提供了不同的線程池:IO Computation
Android線程優(yōu)化實(shí)戰(zhàn)
- 嚴(yán)禁使用new Thread方式歉提。
- 提供基礎(chǔ)線程池供各個(gè)業(yè)務(wù)線使用,避免各個(gè)業(yè)務(wù)線各自維護(hù)一套線程池区转,導(dǎo)致線程數(shù)過多苔巨。
- 根據(jù)任務(wù)類型選擇合適的異步方式:優(yōu)先級(jí)低,長(zhǎng)時(shí)間執(zhí)行废离,HandlerThread侄泽;定時(shí)執(zhí)行耗時(shí)任務(wù),線程池蜻韭。
- 創(chuàng)建線程必須命名悼尾,以方便定位線程歸屬,在運(yùn)行期Thread.currentThread().setName修改名字肖方。
- 關(guān)鍵異步任務(wù)監(jiān)控闺魏,注意異步不等于不耗時(shí),建議使用AOP的方式來做監(jiān)控俯画。
- 重視優(yōu)先級(jí)設(shè)置(根據(jù)任務(wù)具體情況)析桥,Process.setThreadPriority();可以設(shè)置多次。
線程收斂?jī)?yōu)雅實(shí)踐初步
- 基礎(chǔ)庫內(nèi)部暴露API:setExecutor艰垂。
- 初始化的時(shí)候注入統(tǒng)一的線程庫泡仗。
布局優(yōu)化
- 減少層級(jí)
- 合理使用RelativeLayout和LinearLayout,RelativeLayout會(huì)對(duì)子View做兩次測(cè)量材泄。但如果在LinearLayout中有weight屬性沮焕,也需要進(jìn)行兩次測(cè)量,因?yàn)闆]有更多的依賴關(guān)系拉宗,所以仍然會(huì)比RelativeLayout的效率高峦树。
- 合理使用Merge
- 提供顯示速度
- ViewStub
- 布局復(fù)用
- 通過標(biāo)簽來實(shí)現(xiàn)
- 其他
- 使用標(biāo)簽加載一些不常用的布局辣辫。
- 盡可能少用wrap_content,wrap_content會(huì)增加布局measure時(shí)的計(jì)算成本魁巩,已知寬高為固定值時(shí)急灭,不用wrap_content。
- 使用TextView替換RL谷遂、LL葬馋。
- 使用低端機(jī)進(jìn)行優(yōu)化,以發(fā)現(xiàn)性能瓶頸肾扰。
- 使用TextView的行間距替換多行文本:lineSpacingExtra/lineSpacingMultiplier畴嘶。
- 使用Spannable/Html.fromHtml替換多種不同規(guī)格文字。
- 盡可能使用LinearLayout自帶的分割線集晚。
- 使用Space添加間距窗悯。
- 多利用lint + alibaba規(guī)約修復(fù)問題點(diǎn)。
- 嵌套層級(jí)過多可以考慮使用約束布局
減少過度繪制
導(dǎo)致過度繪制的主要原因是:
- XML布局:控件有重疊且都有設(shè)置背景偷拔。
- View自繪:View.OnDraw里面同一個(gè)區(qū)域被繪制多次蒋院。
如何避免過度繪制
-
布局上的優(yōu)化
- 移除XML中非必需的背景
- 有選擇性地移除窗口背景:getWindow().setBackgroundDrawable(null)
- 按需顯示占位背景圖片
-
自定義View優(yōu)化
- 通過canvas.clipRect()來幫助系統(tǒng)識(shí)別那些可見的區(qū)域
- 繪制一個(gè)單元之前,首先判斷該單元的區(qū)域是否在Canvas的剪切域內(nèi)莲绰。若不在欺旧,直接返回
webview的優(yōu)化
- WebView首次創(chuàng)建比較耗時(shí),需要預(yù)先創(chuàng)建WebView提前將其內(nèi)核初始化蛤签。
- 使用WebView緩存池辞友,用到WebView的時(shí)候都從緩存池中拿,注意內(nèi)存泄漏問題震肮。
- 本地離線包踏枣,即預(yù)置靜態(tài)頁面資源
- 本地接口請(qǐng)求,緩存
- DNS解析優(yōu)化
- 另外開啟webView進(jìn)程
- 首屏靜態(tài)html
- 骨架屏
fresco加載圖片原理 優(yōu)勢(shì)是什么
緩存怎么處理的
a钙蒙、根據(jù)Uri在已解碼的(Bitmap緩存)內(nèi)存緩存中查找茵瀑,找到了則返回Bitmap對(duì)象;如果沒找到躬厌,則開啟后臺(tái)線程開始后續(xù)的工作马昨。
b、根據(jù)Uri在未解碼的內(nèi)存緩存中查找扛施,若找到了則解碼鸿捧,然后緩存到已解碼的內(nèi)存緩存中,并且返回Bitmap對(duì)象疙渣。
d匙奴、如果在未解碼的內(nèi)存緩存中沒找到,則根據(jù)Uri在磁盤緩存中查找妄荔,若找到了則讀取數(shù)據(jù)(byte數(shù)組)泼菌,并緩存到未解碼的內(nèi)存緩存中谍肤,解碼哗伯、然后緩存到已解碼的內(nèi)存緩存中荒揣,并且返回Bitmap對(duì)象。
e焊刹、如果在磁盤緩存中沒找到系任,則從網(wǎng)絡(luò)或者本地加載數(shù)據(jù)。加載完成后虐块,依次緩存到磁盤緩存俩滥、未解碼的內(nèi)存緩存中。解碼贺奠、然后緩存到已解碼的內(nèi)存緩存中举农,并且返回Bitmap對(duì)象。
bitmap 內(nèi)存分配
- 在4.x及以下的系統(tǒng)上敞嗡,F(xiàn)resco的bitmap decode會(huì)把bitmap的pixel data(像素?cái)?shù)據(jù))放到一個(gè)“特殊的內(nèi)存區(qū)域’ ”,這個(gè)特殊的內(nèi)存區(qū)域其實(shí)就是ashmem
- 為什么在4.x及以下系統(tǒng)需要這樣做航背?原因是如果Bitmap數(shù)量很多時(shí)會(huì)占用大量的內(nèi)存(這里內(nèi)存特指Java Heap)喉悴,必然就會(huì)更加頻繁的觸發(fā)虛擬機(jī)進(jìn)行GC,GC 會(huì)導(dǎo)致stop the world
- 怎么實(shí)現(xiàn)的玖媚?
- BitmapFactory.Options 的參數(shù) inPurgeable能使bitmap的內(nèi)存分配到ashmem上箕肃。inPurgeable的作用是,在KITKAT及以下今魔,使用該參數(shù)的話勺像,當(dāng)系統(tǒng)需要回收內(nèi)存的時(shí)候,bitmap的pixels可以被清除错森。好在的是吟宦,當(dāng)pixels需要被重新訪問的時(shí)候(例如bitmap draw或者調(diào)用getPixels()的時(shí)候),它們又可以重新被decode出來
- isPurgeable為true的圖片涩维,內(nèi)存分配的時(shí)機(jī)和流程,只有當(dāng)bitmap進(jìn)行draw或者getPixels(這個(gè)大抵一致)的時(shí)候,bitmap 的pixels才進(jìn)行實(shí)際的內(nèi)存分配
- bitmap draw的整個(gè)流程看耻蛇,操作都處于UI線程纤子,由于存在耗時(shí)的操作,會(huì)導(dǎo)致drop frames
- ART模式下睡蟋,BitmapOptions的inBitmap和inTempStorage去優(yōu)化內(nèi)存使用踏幻。inBitmap是由上層的BitmapPool 去分配內(nèi)存,inTempStorage是由 SynchronizedPool分配內(nèi)存戳杀,都是用緩存池的方式分配和回收內(nèi)存该面,做到對(duì)這些區(qū)域的內(nèi)存可管理夭苗,減少各個(gè)不同地方自行分配內(nèi)存 。
- 為啥使用 Ashmem
- Java Heap:大小受系統(tǒng)限制吆倦,內(nèi)存自動(dòng)回收听诸。
- Native Heap:大小不受系統(tǒng)限制,僅受物理內(nèi)存限制蚕泽,內(nèi)存需要手動(dòng)釋放晌梨。
- Ashmem:大小不受系統(tǒng)限制,僅受物理內(nèi)存限制须妻,系統(tǒng)在需要的時(shí)候可以自動(dòng)回收仔蝌,也可以手動(dòng)阻止回收。
多進(jìn)程通訊的方式
- bundle 四大組件可以通過這種方式傳遞數(shù)據(jù)
- 文件共享 簡(jiǎn)單的文件讀寫荒吏,序列化文件敛惊,sp
- AIDL 客戶端,服務(wù)端绰更,接口
- Messenger 底層是binder瞧挤,客戶端和服務(wù)端兩端,主要是對(duì)消息的處理儡湾,串行的方式處理消息
- ContentProvider 底層是binder特恬,和數(shù)據(jù)庫操作息息相關(guān)
- Socket serverSocket
Handler 如何規(guī)避內(nèi)存泄漏
- 原因:非靜態(tài)內(nèi)部類會(huì)隱性地持有外部類的引用
- 監(jiān)控內(nèi)存泄漏
- LeakCanary
- 原理,利用了Java的WeakReference和ReferenceQueue徐钠,通過將Activity包裝到WeakReference中癌刽,被WeakReference包裝過的Activity對(duì)象如果被回收,該WeakReference引用會(huì)被放到ReferenceQueue中尝丐,通過監(jiān)測(cè)ReferenceQueue里面的內(nèi)容就能檢查到Activity是否能夠被回收
- 解決方法
- 在onDestory中移除 handler.removeCallbacksAndMessages(null)
- 聲明一個(gè)靜態(tài)的Handler內(nèi)部類显拜,并持有外部類的弱引用
data binding
-
原理
- 將xml文件拆分成兩部分,一部分是數(shù)據(jù)文件爹袁,一部分是布局文件远荠,在布局文件中,為每個(gè)View設(shè)置一個(gè)tag失息,數(shù)據(jù)文件中矮台,標(biāo)示了相對(duì)應(yīng)的tag的View所需要使用的數(shù)據(jù)
- 根據(jù)上面的兩個(gè)xml在編譯時(shí)生成 ActivityDataBindBinding和BR類
- 在 ActivityDataBindBinding 當(dāng)中,會(huì)更加tag對(duì)每一個(gè)View進(jìn)行賦值
- 根據(jù)上面的xml對(duì)數(shù)據(jù)進(jìn)行綁定操作 (executeBindings方法)
- 數(shù)據(jù)的實(shí)時(shí)刷新
- 在進(jìn)行set的方法時(shí)候根时,最終會(huì)調(diào)用到 executeBindings方法
Android中動(dòng)畫的原理
-
幀動(dòng)畫
- animation-list --> AnimationDrawable
- 容易o(hù)om
-
view動(dòng)畫
- a.getTransformation方法會(huì)返回一個(gè)布爾值瘦赫,表示是否需要繼續(xù)動(dòng)畫,一旦為true蛤迎,則父控件會(huì)重新計(jì)算需要?jiǎng)赢嬓枰⑿碌膮^(qū)域并且更新該區(qū)域确虱。在動(dòng)畫結(jié)束之前會(huì)不斷重繪,從而形成連續(xù)的動(dòng)畫效果替裆。
- getTransformation 在這個(gè)方法中校辩,根據(jù)流逝的時(shí)間計(jì)算當(dāng)前動(dòng)畫時(shí)間百分比窘问,然后通過插值器(Interpolator)重新計(jì)算這個(gè)百分比,并且以此來計(jì)算當(dāng)前動(dòng)畫屬性值
-
屬性動(dòng)畫
- 屬性動(dòng)畫是通過VSYNC信號(hào)來持續(xù)改變屬性值進(jìn)行動(dòng)畫的
- 相比于補(bǔ)間動(dòng)畫宜咒,屬性動(dòng)畫重繪明顯會(huì)少很多
- 流程 ObjectAnimator.ofFloat(button,"translationX",0,200);
- 首先會(huì)把我們傳入的View保存起來
- 然后會(huì)初始化一個(gè)Holder對(duì)象惠赫,這個(gè)Holder對(duì)象是用來干啥的呢?我們傳入了一個(gè)translationX值故黑,Holder對(duì)象中提供一個(gè)方法儿咱,把translationX拼接成一個(gè)set方法,setTranslationX场晶,然后通過反射找到View中的此方法混埠。當(dāng)數(shù)值計(jì)算出來之后,執(zhí)行獲取的這個(gè)方法诗轻。
- KeyframeSet是一個(gè)關(guān)鍵幀的集合钳宪,最后我們傳入0-200就是關(guān)鍵幀
- 插值器,用來計(jì)算某一時(shí)間中動(dòng)畫長(zhǎng)度播放的百分比
- 估值器扳炬,根據(jù)插值器計(jì)算的百分比吏颖,來計(jì)算某個(gè)時(shí)間,動(dòng)畫所要更新的值恨樟。然后交給Holder執(zhí)行動(dòng)畫
- 屬性動(dòng)畫會(huì)監(jiān)聽系統(tǒng)發(fā)出的VSYNC信號(hào)半醉,每收到一次信號(hào)就執(zhí)行一次
-
插值器和估值器
- 插值器:設(shè)置 屬性值 從初始值過渡到結(jié)束值 的變化規(guī)律
- 估值器:設(shè)置 屬性值 從初始值過渡到結(jié)束值 的變化具體數(shù)值
- 插值器只是根據(jù)時(shí)間百分比計(jì)算出一個(gè)屬性值百分比,而把屬性值百分比轉(zhuǎn)換為真正屬性值則交給估值器來做厌杜。例如:插值器返回的是一個(gè)百分比,估值器就可以將這個(gè)百分比轉(zhuǎn)換成色值(0-255)或者一個(gè)坐標(biāo)
字符串hash函數(shù)
- 源碼
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
-
為什么是31
- 31是一個(gè)不大不小的質(zhì)數(shù)计螺,是作為 hashCode 乘子的優(yōu)選質(zhì)數(shù)之一夯尽,為啥選擇質(zhì)數(shù),質(zhì)數(shù)可以降低哈希算法的沖突率
- 31可以被 JVM 優(yōu)化登馒,31 * i = (i << 5) - i
- Effective Java : 選擇數(shù)字31是因?yàn)樗且粋€(gè)奇質(zhì)數(shù)匙握,如果選擇一個(gè)偶數(shù)會(huì)在乘法運(yùn)算中產(chǎn)生溢出,導(dǎo)致數(shù)值信息丟失陈轿,因?yàn)槌硕喈?dāng)于移位運(yùn)算圈纺。選擇質(zhì)數(shù)的優(yōu)勢(shì)并不是特別的明顯,但這是一個(gè)傳統(tǒng)麦射。同時(shí)蛾娶,數(shù)字31有一個(gè)很好的特性,即乘法運(yùn)算可以被移位和減法運(yùn)算取代潜秋,來獲取更好的性能:31 * i == (i << 5) - i蛔琅,現(xiàn)代的 Java 虛擬機(jī)可以自動(dòng)的完成這個(gè)優(yōu)化。
ThreadLocal
- ThreadLocal是并發(fā)場(chǎng)景下用來解決變量共享問題的類峻呛,它能使原本線程間共享的對(duì)象進(jìn)行線程隔離罗售,即一個(gè)對(duì)象只對(duì)一個(gè)線程可見
- 當(dāng)設(shè)置value時(shí)辜窑,變量的值保存在一個(gè)與線程相關(guān)的map中(ThreadLocalMap),這樣做是為了避免多線程競(jìng)爭(zhēng)寨躁,因?yàn)榉旁赥hread對(duì)象中就相當(dāng)于線程私有了穆碎,處理的時(shí)候不需要加鎖
- ThreadLocalMap里面的 Entry extends WeakReference<ThreadLocal<?>>
- 存在的潛在問題
- 如果任務(wù)對(duì)象結(jié)束而線程實(shí)例仍然存在(常見于線程池的使用中,需要復(fù)用線程實(shí)例)职恳,那么仍然會(huì)發(fā)生內(nèi)存泄露所禀。
- 線程復(fù)用會(huì)產(chǎn)生臟數(shù)據(jù)
- ThreadLocalMap在它的getEntry、set话肖、remove北秽、rehash等方法中都會(huì)主動(dòng)清除ThreadLocalMap中key為null的Entry,但如果我們聲明ThreadLocal變量后最筒,再也沒有調(diào)用過上述方法贺氓,依然會(huì)發(fā)生內(nèi)存泄露
- 參考
編碼格式
- 文字到0、1的映射稱為編碼床蜘,反過來從0辙培、1到文字叫解碼。
- 最早的計(jì)算機(jī)在設(shè)計(jì)時(shí)采用8個(gè)比特(bit)作為一個(gè)字節(jié)(byte)邢锯,所以扬蕊,一個(gè)字節(jié)能表示的最大的整數(shù)就是255(二進(jìn)制11111111=十進(jìn)制255),0 - 255被用來表示大小寫英文字母丹擎、數(shù)字和一些符號(hào)尾抑,這個(gè)編碼表被稱為ASCII編碼
- Unicode編碼定義了這個(gè)世界上幾乎所有字符的數(shù)字表示,已經(jīng)擴(kuò)展到了 21 位
- Unicode給這串?dāng)?shù)字ID起了個(gè)名字叫[碼點(diǎn)]蒂培。[碼點(diǎn)]經(jīng)過映射后得到的二進(jìn)制串的轉(zhuǎn)換格式單位稱之為[碼元]
- [碼點(diǎn)]就是一串二進(jìn)制數(shù)再愈,【碼元】就是切分這個(gè)二進(jìn)制數(shù)的方法。
- 編碼空間被分成 17 個(gè)平面(plane)护戳,每個(gè)平面有 65,536 個(gè)字符(2個(gè)字節(jié)翎冲,16位)。0 號(hào)平面叫做「基本多文種平面」(BMP)媳荒,涵蓋了幾乎所有你能遇到的字符抗悍,除了 emoji(emoji位于1號(hào)平面 - -)。其它平面叫做補(bǔ)充平面钳枕,大多是空的
- UTF-32 UTF-32也就是說它的碼元是32位缴渊,每32位去讀一下碼點(diǎn)
- UTF-16 它的碼元是16位的,也就是說每16位去讀一下碼點(diǎn)鱼炒,獲取碼點(diǎn)的前16位數(shù)字疟暖,直到讀取完成。
- BMP平面(plane0)中的每一個(gè)碼點(diǎn)都直接與一個(gè)UTF-16 的碼元一一映射。
- 其它平面里很少使用的碼點(diǎn)都是用兩個(gè) 16 位的碼元來編碼的
- UTF-8 使用一到四個(gè)字節(jié)來編碼一個(gè)碼點(diǎn)
- 從 0 到 127 的這些碼點(diǎn)直接映射成 1 個(gè)字節(jié)俐巴,西文骨望,都位于此段,該編碼方式非常節(jié)約空間
- 接下來的 1,920 個(gè)碼點(diǎn)映射成 2 個(gè)字節(jié)
- 在 BMP 里所有剩下的碼點(diǎn)需要 3 個(gè)字節(jié) 對(duì)于中文欣舵,就位于此段擎鸠,3個(gè)字節(jié)
- 參考
volatile
用于保持內(nèi)存可見性(隨時(shí)見到的都是最新值)和防止指令重排序 參考
java內(nèi)存模型
- 主內(nèi)存,工作內(nèi)存
- 主內(nèi)存:虛擬機(jī)中的一塊內(nèi)存缘圈,對(duì)應(yīng)java堆中的對(duì)象實(shí)例數(shù)據(jù)
- 工作內(nèi)存:每條線程自己的內(nèi)存空間劣光,保存了該線程使用到的變量的主內(nèi)存副本拷貝,線程對(duì)變量的所有操作糟把,都必須在工作內(nèi)存中進(jìn)行绢涡,不能直接讀取主內(nèi)存中的變量,對(duì)應(yīng)虛擬機(jī)棧中的部分區(qū)域
- 內(nèi)存間的交互
- lock:作用于主內(nèi)存遣疯,把變量標(biāo)識(shí)為線程獨(dú)占狀態(tài)雄可。
- unlock:作用于主內(nèi)存,解除獨(dú)占狀態(tài)缠犀。
- read:作用主內(nèi)存数苫,把一個(gè)變量的值從主內(nèi)存?zhèn)鬏數(shù)骄€程的工作內(nèi)存。
- load:作用于工作內(nèi)存辨液,把read操作傳過來的變量值放入工作內(nèi)存的變量副本中虐急。
- use:作用工作內(nèi)存,把工作內(nèi)存當(dāng)中的一個(gè)變量值傳給執(zhí)行引擎滔迈。
- assign:作用工作內(nèi)存止吁,把一個(gè)從執(zhí)行引擎接收到的值賦值給工作內(nèi)存的變量。
- store:作用于工作內(nèi)存的變量燎悍,把工作內(nèi)存的一個(gè)變量的值傳送到主內(nèi)存中敬惦。
- write:作用于主內(nèi)存的變量,把store操作傳來的變量的值放入主內(nèi)存的變量中间涵。
- 可見性:當(dāng)一條線程修改了某個(gè)變量的值仁热,新值對(duì)于其他線程來說是可以立即得知的
- 普通變量的值是線程間傳遞需要通過主內(nèi)存來完成的榜揖,一個(gè)值在一個(gè)線程被修改勾哩,需要向主內(nèi)存進(jìn)行回寫,另一條線程在去從主內(nèi)存中進(jìn)行讀取操作举哟,新值才會(huì)對(duì)另外一條線程可見
volatile保持可見性
- 關(guān)鍵字修飾的變量看到的隨時(shí)是自己的最新值
- volatile的特殊規(guī)則就是:
- read思劳、load、use動(dòng)作必須連續(xù)出現(xiàn)妨猩。
- assign潜叛、store、write動(dòng)作必須連續(xù)出現(xiàn)。
- 使用volatile變量能夠保證:
- 每次讀取前必須先從主內(nèi)存刷新最新的值威兜。
- 每次寫入后必須立即同步回主內(nèi)存當(dāng)中销斟。
- 注意:volatile關(guān)鍵字使變量的讀、寫具有了“原子性”椒舵。然而這種原子性僅限于變量(包括引用)的讀和寫蚂踊,無法涵蓋變量上的任何操作,即:
- 基本類型的自增(如count++)等操作不是原子的笔宿。
- 對(duì)象的任何非原子成員調(diào)用(包括成員變量和成員方法)不是原子的犁钟。
防止指令重排
指令重排序是cpu采用了允許將多條指令不按照程序規(guī)定的順序分開發(fā)送給各相應(yīng)電路單元來處理
volatile關(guān)鍵字通過“內(nèi)存屏障”來防止指令被重排序 (只有在Happens-Before內(nèi)存模型中才會(huì)出現(xiàn)指令重排)
volatile 讀操作的性能消耗與普通變量幾乎沒有什么差別,寫操作可能會(huì)慢一些泼橘,因?yàn)樗枰诒镜卮a中插入許多內(nèi)存屏障指令來保證處理器不返生亂序執(zhí)行
來看一個(gè)單利模式 DCL(Double Check Lock涝动,雙重檢查鎖)
class Singleton {
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance() {
if ( instance == null ) { //當(dāng)instance不為null時(shí),仍可能指向一個(gè)“被部分初始化的對(duì)象”
synchronized (Singleton.class) {
if ( instance == null ) {
instance = new Singleton();
}
}
}
return instance;
}
}
它可以”抽象“為下面幾條JVM指令:
memory = allocate(); //1:分配對(duì)象的內(nèi)存空間
initInstance(memory); //2:初始化對(duì)象
instance = memory; //3:設(shè)置instance指向剛分配的內(nèi)存地址
JVM可以以“優(yōu)化”為目的對(duì)它們進(jìn)行重排序炬灭,經(jīng)過重排序后如下:
memory = allocate(); //1:分配對(duì)象的內(nèi)存空間
instance = memory; //3:設(shè)置instance指向剛分配的內(nèi)存地址(此時(shí)對(duì)象還未初始化)
ctorInstance(memory); //2:初始化對(duì)象
引用instance指向了一個(gè)"被部分初始化的對(duì)象"醋粟。此時(shí),如果另一個(gè)線程調(diào)用getInstance方法担败,由于instance已經(jīng)指向了一塊內(nèi)存空間昔穴,從而if條件判為false,方法返回instance引用提前,用戶得到了沒有完成初始化的“半個(gè)”單例吗货。
解決這個(gè)該問題,只需要將instance聲明為volatile變量:
private static volatile Singleton instance;
Thread類的sleep() yield() 和 wait()的區(qū)別?
- sleep()方法(休眠)是線程類(Thread)的靜態(tài)方法狈网,調(diào)用此方法會(huì)讓當(dāng)前線程暫停執(zhí)行指定的時(shí)間宙搬,將執(zhí)行機(jī)會(huì)(CPU)讓給其他線程,但是對(duì)象的鎖依然保持拓哺,因此休眠時(shí)間結(jié)束后會(huì)自動(dòng)恢復(fù)
- wait()是Object類的方法勇垛,調(diào)用對(duì)象的wait()方法導(dǎo)致當(dāng)前線程放棄對(duì)象的鎖(線程暫停執(zhí)行),進(jìn)入對(duì)象的等待池(wait pool)士鸥,只有調(diào)用對(duì)象的notify()方法(或notifyAll()方法)時(shí)才能喚醒等待池中的線程進(jìn)入等鎖池(lock pool)闲孤,如果線程重新獲得對(duì)象的鎖就可以進(jìn)入就緒狀態(tài)。
- sleep()方法給其他線程運(yùn)行機(jī)會(huì)時(shí)不考慮線程的優(yōu)先級(jí)烤礁,因此會(huì)給低優(yōu)先級(jí)的線程以運(yùn)行的機(jī)會(huì)讼积;yield()方法只會(huì)給相同優(yōu)先級(jí)或更高優(yōu)先級(jí)的線程以運(yùn)行的機(jī)會(huì)
多線程如何保證線程安全
- 同步 Synchronized 參考
- 普通同步方法,鎖是當(dāng)前實(shí)例對(duì)象.JVM可以從方法常量池中的方法表結(jié)構(gòu)(method_info Structure) 中的 ACC_SYNCHRONIZED 訪問標(biāo)志區(qū)分一個(gè)方法是否同步方法
- 靜態(tài)同步方法脚仔,鎖時(shí)當(dāng)前類的Class對(duì)象勤众。
- 代碼塊同步:指定加鎖對(duì)象,對(duì)給定對(duì)象加鎖鲤脏,進(jìn)入同步代碼庫前要獲得給定對(duì)象的鎖们颜。使用monitorenter和monitorexit指令實(shí)現(xiàn)吕朵。
- 使用原子類(atomic concurrent classes) 如AtomicInteger等,AtomicInteger通過聲明一個(gè)volatile value(內(nèi)存鎖定窥突,同一時(shí)刻只有一個(gè)線程可以修改內(nèi)存值)類型的變量努溃,再加上unsafe.compareAndSwapInt的方法,來保證實(shí)現(xiàn)線程同步的阻问。
- 實(shí)現(xiàn)并發(fā)鎖
- 使用volatile關(guān)鍵字
- 使用不變類和線程安全類
GC
找到被回收的對(duì)象
- 引用計(jì)數(shù)法
- 堆中每個(gè)對(duì)象(不是引用)都有一個(gè)引用計(jì)數(shù)器茅坛。當(dāng)一個(gè)對(duì)象被創(chuàng)建并初始化賦值后,該變量計(jì)數(shù)設(shè)置為1则拷。每當(dāng)有一個(gè)地方引用它時(shí)贡蓖,計(jì)數(shù)器值就加1(a = b, b被引用煌茬,則b引用的對(duì)象計(jì)數(shù)+1)斥铺。當(dāng)引用失效時(shí)(一個(gè)對(duì)象的某個(gè)引用超過了生命周期(出作用域后)或者被設(shè)置為一個(gè)新值時(shí)),計(jì)數(shù)器值就減1坛善。任何引用計(jì)數(shù)為0的對(duì)象可以被當(dāng)作垃圾收集晾蜘。當(dāng)一個(gè)對(duì)象被垃圾收集時(shí),它引用的任何對(duì)象計(jì)數(shù)減1眠屎。
- 優(yōu)點(diǎn):引用計(jì)數(shù)收集器執(zhí)行簡(jiǎn)單剔交,判定效率高,交織在程序運(yùn)行中改衩。
- 缺點(diǎn): 難以檢測(cè)出對(duì)象之間的循環(huán)引用岖常。同時(shí),引用計(jì)數(shù)器增加了程序執(zhí)行的開銷
- 早期的JVM使用引用計(jì)數(shù)
- 可達(dá)性分析法
- 通過一系列名為“GC Roots”的對(duì)象作為起始點(diǎn)葫督,從這些節(jié)點(diǎn)開始向下繼續(xù)尋找它們的引用節(jié)點(diǎn)竭鞍,搜索所走過的路徑稱為引用鏈,當(dāng)一個(gè)對(duì)象到GC Roots沒有任何引用鏈相連時(shí)橄镜,就證明此對(duì)象是不可用的偎快。
- GC Roots對(duì)象:虛擬機(jī)棧中引用的對(duì)象;方法區(qū)中類靜屬性引用的對(duì)象洽胶;方法區(qū)中常量引用的對(duì)象晒夹;本地方法中JNI引用的對(duì)象
- 真正宣告一個(gè)對(duì)象死亡,至少要經(jīng)歷兩次標(biāo)記過程:
- 如果對(duì)象在進(jìn)行根搜索后發(fā)現(xiàn)沒有與GC Roots相連接的引用鏈姊氓,那它會(huì)被第一次標(biāo)記并且進(jìn)行一次篩選丐怯。篩選的條件是此對(duì)象是否有必要執(zhí)行 finalize()方法。當(dāng)對(duì)象沒有覆蓋finalize()方法他膳,或finalize()方法已經(jīng)被虛擬機(jī)調(diào)用過响逢,虛擬機(jī)將這兩種情況都視為沒有必要執(zhí)行绒窑。
- 如果該對(duì)象被判定為有必要執(zhí)行finalize()方法棕孙,那么這個(gè)對(duì)象將會(huì)被放置在一個(gè)名為F-Queue隊(duì)列中,并在稍后由一條由虛擬機(jī)自動(dòng)建立的、低優(yōu)先級(jí)的Finalizer線程去執(zhí)行finalize()方法蟀俊。finalize()方法是對(duì)象逃脫死亡命運(yùn)的最后一次機(jī)會(huì)钦铺,稍后GC將對(duì)F-Queue中的對(duì)象進(jìn)行第二次小規(guī)模的標(biāo)記,如果要在finalize()方法中成功拯救自己肢预,只要在finalize()方法中讓該對(duì)象重新引用鏈上的任何一個(gè)對(duì)象建立關(guān)聯(lián)即可矛洞。而如果對(duì)象這時(shí)還沒有關(guān)聯(lián)到任何鏈上的引用,那它就會(huì)被回收掉烫映。
回收算法
-
標(biāo)記清除算法
- 首先標(biāo)記出所需回收的對(duì)象沼本,在標(biāo)記完成后統(tǒng)一回收掉所有被標(biāo)記的對(duì)象
- 優(yōu)點(diǎn):不需要進(jìn)行對(duì)象的移動(dòng),并且僅對(duì)不存活的對(duì)象進(jìn)行處理锭沟,在存活對(duì)象比較多的情況下極為高效抽兆。
- 缺點(diǎn):
- 標(biāo)記和清除過程的效率都不高。這種方法需要使用一個(gè)空閑列表來記錄所有的空閑區(qū)域以及大小族淮。對(duì)空閑列表的管理會(huì)增加分配對(duì)象時(shí)的工作量辫红。
- 標(biāo)記清除后會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片
-
復(fù)制算法
- 它將內(nèi)存按容量分為大小相等的兩塊,每次只使用其中的一塊(對(duì)象面)祝辣,當(dāng)這一塊的內(nèi)存用完了贴妻,就將還存活著的對(duì)象復(fù)制到另外一塊內(nèi)存上面(空閑面),然后再把已使用過的內(nèi)存空間一次清理掉蝙斜。
- 優(yōu)點(diǎn):
- (1)標(biāo)記階段和復(fù)制階段可以同時(shí)進(jìn)行名惩。
- (2)每次只對(duì)一塊內(nèi)存進(jìn)行回收,運(yùn)行高效孕荠。
- (3)只需移動(dòng)棧頂指針绢片,按順序分配內(nèi)存即可,實(shí)現(xiàn)簡(jiǎn)單岛琼。
- (4)內(nèi)存回收時(shí)不用考慮內(nèi)存碎片的出現(xiàn)
- 缺點(diǎn):需要一塊能容納下所有存活對(duì)象的額外的內(nèi)存空間底循。因此,可一次性分配的最大內(nèi)存縮小了一半槐瑞。
- 復(fù)制算法比較適合于新生代(短生存期的對(duì)象)熙涤,在老年代(長(zhǎng)生存期的對(duì)象)中,對(duì)象存活率比較高困檩,如果執(zhí)行較多的復(fù)制操作祠挫,效率將會(huì)變低,所以老年代一般會(huì)選用其他算法悼沿,如標(biāo)記—整理算法
-
標(biāo)記—整理算法
- 標(biāo)記的過程與標(biāo)記—清除算法中的標(biāo)記過程一樣等舔,但對(duì)標(biāo)記后出的垃圾對(duì)象的處理情況有所不同,它不是直接對(duì)可回收對(duì)象進(jìn)行清理糟趾,而是讓所有的對(duì)象都向一端移動(dòng)慌植,然后直接清理掉端邊界以外的內(nèi)存
- 優(yōu)點(diǎn):
- (1)經(jīng)過整理之后甚牲,新對(duì)象的分配只需要通過指針碰撞便能完成(Pointer Bumping),相當(dāng)簡(jiǎn)單蝶柿。
- (2)使用這種方法空閑區(qū)域的位置是始終可知的丈钙,也不會(huì)再有碎片的問題了。
- 缺點(diǎn):GC暫停的時(shí)間會(huì)增長(zhǎng)交汤,因?yàn)槟阈枰獙⑺械膶?duì)象都拷貝到一個(gè)新的地方雏赦,還得更新它們的引用地址。
分代收集
Java的堆內(nèi)存劃分:新生代芙扎、年老代和持久代星岗。新生代又被進(jìn)一步劃分為Eden和Survivor區(qū),最后Survivor由FromSpace(Survivor0)和ToSpace(Survivor1)組成
-
年輕代:
- 幾乎所有新生成的對(duì)象首先都是放在年輕代的戒洼。
- 新生代內(nèi)存按照8:1:1的比例分為一個(gè)Eden區(qū)和兩個(gè)Survivor(Survivor0,Survivor1)區(qū)伍茄。
- 大部分對(duì)象在Eden區(qū)中生成。 當(dāng)新對(duì)象生成施逾,Eden Space申請(qǐng)失敺蠼谩(因?yàn)榭臻g不足等),則會(huì)發(fā)起一次GC(Scavenge GC)汉额。
- 回收時(shí)先將Eden區(qū)存活對(duì)象復(fù)制到一個(gè)Survivor0區(qū)曹仗,然后清空Eden區(qū),當(dāng)這個(gè)Survivor0區(qū)也存放滿了時(shí)蠕搜,則將Eden區(qū)和Survivor0區(qū)存活對(duì)象復(fù)制到另一個(gè)Survivor1區(qū)怎茫,然后清空Eden和這個(gè)Survivor0區(qū),此時(shí)Survivor0區(qū)是空的妓灌,然后將Survivor0區(qū)和Survivor1區(qū)交換轨蛤,即保持Survivor1區(qū)為空, 如此往復(fù)虫埂。
- 當(dāng)Survivor1區(qū)不足以存放 Eden和Survivor0的存活對(duì)象時(shí)祥山,就將存活對(duì)象直接存放到老年代。當(dāng)對(duì)象在Survivor區(qū)躲過一次GC的話掉伏,其對(duì)象年齡便會(huì)加1缝呕,默認(rèn)情況下,如果對(duì)象年齡達(dá)到15歲斧散,就會(huì)移動(dòng)到老年代中供常。
-
年老代
- 在年輕代中經(jīng)歷了N次垃圾回收后仍然存活的對(duì)象,就會(huì)被放到年老代中鸡捐。因此栈暇,可以認(rèn)為年老代中存放的都是一些生命周期較長(zhǎng)的對(duì)象。內(nèi)存比新生代也大很多(大概比例是1:2)
- 當(dāng)老年代內(nèi)存滿時(shí)觸發(fā)Major GC即Full GC箍镜,F(xiàn)ull GC發(fā)生頻率比較低源祈,老年代對(duì)象存活時(shí)間比較長(zhǎng)煎源,存活率標(biāo)記高
- 一般來說,大對(duì)象會(huì)被直接分配到老年代新博。所謂的大對(duì)象是指需要大量連續(xù)存儲(chǔ)空間的對(duì)象,最常見的一種大對(duì)象就是大數(shù)組
-
持久代
- 用于存放靜態(tài)文件(class類脚草、方法)和常量等赫悄。持久代對(duì)垃圾回收沒有顯著影響,但是有些應(yīng)用可能動(dòng)態(tài)生成或者調(diào)用一些class馏慨,例如Hibernate 等埂淮,在這種時(shí)候需要設(shè)置一個(gè)比較大的持久代空間來存放這些運(yùn)行過程中新增的類。
- 對(duì)永久代的回收主要回收兩部分內(nèi)容:廢棄常量和無用的類写隶。
- 永久代空間在Java SE8特性中已經(jīng)被移除倔撞。取而代之的是元空間(MetaSpace)。因此不會(huì)再出現(xiàn)“java.lang.OutOfMemoryError: PermGen error”錯(cuò)誤慕趴。
-
堆內(nèi)存分配策略
- 對(duì)象優(yōu)先在Eden分配痪蝇。
- 大對(duì)象直接進(jìn)入老年代。
- 長(zhǎng)期存活的對(duì)象將進(jìn)入老年代冕房。
-
分代的回收算法
- 新生代GC(Minor GC/Scavenge GC):發(fā)生在新生代的垃圾收集動(dòng)作躏啰。因?yàn)镴ava對(duì)象大多都具有朝生夕滅的特性,因此Minor GC非常頻繁耙册。在新生代中给僵,每次垃圾收集時(shí)都會(huì)發(fā)現(xiàn)有大量對(duì)象死去,只有少量存活详拙,因此可選用復(fù)制算法來完成收集帝际。
- 老年代GC(Major GC/Full GC):發(fā)生在老年代的垃圾回收動(dòng)作。由于老年代中的對(duì)象生命周期比較長(zhǎng)饶辙,因此Major GC并不頻繁蹲诀。而老年代中因?yàn)閷?duì)象存活率高、沒有額外空間對(duì)它進(jìn)行分配擔(dān)保弃揽,就必須使用標(biāo)記—清除算法或標(biāo)記—整理算法來進(jìn)行回收侧甫。
- 新生代采用空閑指針的方式來控制GC觸發(fā),指針保持最后一個(gè)分配的對(duì)象在新生代區(qū)間的位置蹋宦,當(dāng)有新的對(duì)象要分配內(nèi)存時(shí)披粟,用于檢查空間是否足夠,不夠就觸發(fā)GC冷冗。當(dāng)連續(xù)分配對(duì)象時(shí)守屉,對(duì)象會(huì)逐漸從Eden到Survivor,最后到老年代
其他
-
垃圾回收?qǐng)?zhí)行時(shí)間
- GC分為Scavenge GC和Full GC蒿辙。
- Scavenge GC :發(fā)生在Eden區(qū)的垃圾回收拇泛。
- Full GC :對(duì)整個(gè)堆進(jìn)行整理滨巴,包括Young、Tenured和Perm俺叭。Full GC因?yàn)樾枰獙?duì)整個(gè)堆進(jìn)行回收恭取,所以比Scavenge GC要慢
- 有如下原因可能導(dǎo)致Full GC:
- 1.年老代(Tenured)被寫滿;
- 2.持久代(Perm)被寫滿;
- 3.System.gc()被顯示調(diào)用;
- 4.上一次GC之后Heap的各域分配策略動(dòng)態(tài)變化.
- GC分為Scavenge GC和Full GC蒿辙。
-
相關(guān)函數(shù)
- System.gc(),請(qǐng)求Java的垃圾回收熄守。僅僅是一個(gè)請(qǐng)求(建議)蜈垮。JVM接受這個(gè)消息后,并不是立即做垃圾回收裕照,而只是對(duì)幾個(gè)垃圾回收算法做了加權(quán)攒发,使垃圾回收操作容易發(fā)生,或提早發(fā)生晋南,或回收較多而已惠猿。
- finalize()
- 在finalize()方法返回之后,對(duì)象消失负间,垃圾收集開始執(zhí)行偶妖。
- finalize()的主要用途是釋放一些其他做法開辟的內(nèi)存空間,以及做一些清理工作政溃。其他做法開辟的內(nèi)存空間餐屎,例如:1)由于在分配內(nèi)存的時(shí)候可能采用了類似 C語言的做法,而非JAVA的通常new做法玩祟,(2)打開的文件資源等
- 一旦垃圾回收器準(zhǔn)備好釋放對(duì)象占用的存儲(chǔ)空間腹缩,首先會(huì)去調(diào)用finalize()方法進(jìn)行一些必要的清理工作。只有到下一次再進(jìn)行垃圾回收動(dòng)作的時(shí)候空扎,才會(huì)真正釋放這個(gè)對(duì)象所占用的內(nèi)存空間藏鹊。
-
觸發(fā)主GC的條件
- 當(dāng)應(yīng)用程序空閑時(shí),即沒有應(yīng)用線程在運(yùn)行時(shí),GC會(huì)被調(diào)用。因?yàn)镚C在優(yōu)先級(jí)最低的線程中進(jìn)行,所以當(dāng)應(yīng)用忙時(shí),GC線程就不會(huì)被調(diào)用
- Java堆內(nèi)存不足時(shí),GC會(huì)被調(diào)用转锈。當(dāng)應(yīng)用線程在運(yùn)行,并在運(yùn)行過程中創(chuàng)建新對(duì)象,若這時(shí)內(nèi)存空間不足,JVM就會(huì)強(qiáng)制地調(diào)用GC線程,以便回收內(nèi)存用于新的分配盘寡。若GC一次之后仍不能滿足內(nèi)存分配的要求,JVM會(huì)再進(jìn)行兩次GC作進(jìn)一步的嘗試,若仍無法滿足要求,則 JVM將報(bào)“out of memory”的錯(cuò)誤,Java應(yīng)用將停止。
- 在編譯過程中作為一種優(yōu)化技術(shù)撮慨,Java 編譯器能選擇給實(shí)例賦 null 值竿痰,從而標(biāo)記實(shí)例為可回收
-
減少GC開銷的措施
- 不要顯式調(diào)用System.gc()昼激。 這樣會(huì)增加了間歇性停頓的次數(shù)吠撮。
- 盡量減少臨時(shí)對(duì)象的使用吕座。 少用臨時(shí)變量就相當(dāng)于減少了垃圾的產(chǎn)生,從而延長(zhǎng)了出現(xiàn)第二個(gè)垃圾回收的時(shí)間,減少了主GC的機(jī)會(huì)磁椒。
- 對(duì)象不用時(shí)最好顯式置為Null。 有利于GC收集器判定垃圾,從而提高了GC的效率涎劈。
- 盡量使用StringBuffer,而不用String來累加字符串缤削。 String是固定長(zhǎng)的字符串對(duì)象,累加String對(duì)象時(shí),本質(zhì)上是重新創(chuàng)建新的String對(duì)象
- 能用基本類型如Int,Long,就不用Integer,Long對(duì)象瘦棋。 基本類型變量占用的內(nèi)存資源比相應(yīng)對(duì)象占用的少得多
- 盡量少用靜態(tài)對(duì)象變量。 靜態(tài)變量屬于全局變量,不會(huì)被GC回收,它們會(huì)一直占用內(nèi)存鲜棠。
- 分散對(duì)象創(chuàng)建或刪除的時(shí)間肌厨。 集中在短時(shí)間內(nèi)大量創(chuàng)建新對(duì)象,所需內(nèi)存變多豁陆,會(huì)增加主GC的頻率柑爸。集中刪除對(duì)象會(huì)突然出現(xiàn)了大量的垃圾對(duì)象,會(huì)增加主GC的頻率盒音。