天天按照書本學一點,會把本身的總結思考寫下來,造成輸出,持續更新,立帖爲證java
-- 2020年7月7日 開始第一次學習 -- 2020年7月8日 今天在百忙Rush B中抽出時間,學了點習,計劃明天把本地方法棧和Java堆看完總結完 -- 2020年7月10日 第一次週五學習,也算是有進步,翻了一下書感受好多啊,不知道何時能看完 -- 2020年7月15日 衝鴨!!!!
Java與C++在內存控制方面大相徑庭,由於Java虛擬機有自動內存管理機制,因此Java程序員就犧牲部份內存控制權,來換取編寫程序時的便利。雖然不容易出現內存泄漏和內存溢出問題,但仍是有必要學習點Java虛擬機相關知識,除了在遇到虛擬機問題時能夠快速解決以外,還能夠和別人裝逼(最大的快樂!)
Java虛擬機在運行Java程序的時候,會將內存自動劃分爲不一樣區域,不一樣區域對應的功能、建立銷燬時間也不一樣,有些區域會隨着虛擬機啓動而一直存在,有些區域以來用戶的線程啓動結束而建立銷燬。程序員
內存區域分爲如下幾個區域:數組
思考:了下爲何也是線程私有的?應該時每一個線程執行方法不一樣,裏面的一些臨時變量等也不會相同,爲了在切換線程時不會發生混亂互相干擾,因此須要和程序計數器同樣,也是線程私有的內存空間緩存
本地方法棧與Java虛擬機棧做用類似,但也稍有區別。Java虛擬機棧是爲虛擬機執行Java方法(字節碼)服務的,而本地方法棧是爲虛擬機執行本地方法(Native)服務的。安全
由於在《Java虛擬機規範》中,並無對本地方法棧作強制規定,因此不一樣虛擬機實現的方式可能不一樣,有些虛擬機(HotSpot虛擬機)直接將本地方法棧和Java虛擬機棧合二爲一多線程
與Java虛擬機棧同樣,當本地方法棧深度超出規定(溢出)和棧擴展失敗的時候,也會報StackOverflowError和OutOfMemoryError異常函數
Java堆是虛擬機管理內存中最大的一塊,被全部線程共享,在虛擬機啓動時建立。佈局
主要是負責存放對象實例,按照《Java虛擬機規範》描述是:全部對象實例以及數組都應當在堆上分配。考慮到Java語言的發展,和即時編譯技術的出現,將來可能會出現對象實例不在堆上分配的狀況。性能
Java堆是垃圾收集器管理的內存區域,所以也被稱爲"GC堆"。垃圾收集器大部分是基於分代收集理論設計的,因此會出現新生代、老年代、永久代,Eden空間、Form Survivor空間、To Survivor空間等名詞,這些劃分的區域僅僅是垃圾收集器共同特性或設計風格,並不能說是Java堆是由這些區域組成的。學習
從分配內存角度說,線程共享的Java堆能夠劃分多個線程私有的分配緩衝區(TLAB),劃分出來的惟一做用仍是存放對象實例,目的是爲了更快更好的分配和回收內存。
Java堆在邏輯上是連續的,但在物理上並不要求連續。若是存放的是大對象,例如:數組對象,大多數虛擬機爲了實現簡單、存儲高效,可能會要求連續的存儲空間。
Java堆既能夠是固定大小,也能夠是可擴展的。目前主流虛擬機都是可擴展的,經過參數-Xmx和-Xms設定。若是在Java堆中沒有內存給對象實例分配,而且沒法再擴展時,Java虛擬機將會拋出OutOfMemoryError異常。
在《Java虛擬機規範》中對方法區的約束是十分寬鬆的,許多部分和Java堆相同,例如:
並把方法區描述爲堆的一個邏輯部分,可是方法區和堆仍是有區別的,方法區的另外一個別名叫"非堆(Non-Heap)",方法區用來存放已經被虛擬機加載的類型信息、常量、靜態變量、即時編譯器編譯後的代碼緩存等數據。
方法區與永久代關係
本質上二者並非等價的,但不少人將二者混爲一談,這是由於當初HotSpot虛擬機在設計的時候,爲了簡單方即可以像管理Java堆同樣管理這部份內存,將垃圾收集器的分代設計擴展至方法區,即便用永久代來實現方法區。但Java虛擬機規範中並無對方法區的實現作具體要求,因此其餘虛擬機(如:BEA的JRockit、IBM的J9)都沒有永久代這個概念。
使用永久代實現方法區好處:
能夠像管理Java堆同樣管理一部份內存,省去了專門爲方法區編寫管理代碼的工做
使用永久代實現方法區壞處:
會致使Java應用更容易遇到內存溢出的問題,永久代有-XX:MaxPermSize的上限,即便沒有設置也有默認值,而J9和JRockit只要沒有觸碰到進程可用內存的上限,例如32位系統中4GB限制,就不會出現問題。
有極少數方法(String::intern())會因永久代的緣由而致使不一樣虛擬機下有不一樣表現
永久代介紹
垃圾收集行爲在永久代不多出現,但並非數據進入永久代以後就永久存在了,這一區域內存回收目的主要是針對常量池回收和對類型的卸載,可是由於回收條件嚴格,因此回收效果總不能使人滿意。
當方法區沒法知足新內容內存分配的時候,就會拋出OutOfMemoryError異常。
運行時常量池是方法區的一部分,用來存放編輯時生成的各類字面值和符號引用,由於《Java虛擬機規範》並無對這部分作詳細要求,因此虛擬機開發者能夠按照本身需求去實現這部份內存。除了上面戳的符號引用外,通常還會將符號引用翻譯出來的直接引用也存到運行時常量池中。
具有動態性。並不必定是預置入Class文件中常量池才能進入方法區的運行時常量池,運行期間能夠將新的常量放入。
當沒法申請到足夠內存時,會拋出OutOfMemoryError異常。
直接內存並非虛擬機運行時數據區(上面寫的都是)的一部分,也不是《Java虛擬機規範》中定義的內存。
用力提升性能,避免在Java堆和Native堆中來回複製數據。
直接內存並不受Java堆內存大小的限制,可是受本機總內存的限制。根據實際內存設置-Xmx等參數時,若是忽略直接內存,可能會致使拋出OutOfMemoryError異常。
類加載:Java虛擬機遇到new指令的時候,首先會去常量池定位一個類的符號引用,並檢查這個類是否已經被加載,解析和初始化過。若是沒有則進行類加載過程
分配內存:在類加載以後,就知道對象所須要的內存大小,接下來開始爲對象分配內存。對象分配內存是在堆上完成的,劃出一塊未使用的內存給對象,分配的方式有兩種:"指針碰撞","空閒列表"。到底採用哪一種分配方式取決於Java堆是否規整,Java堆是否規整又取決於垃圾收集器是否帶有空間壓縮整理(Compact)能力。
分配內存中爲了解決線程安全問題有兩種方案:1、對分配內存空間的動做進行同步處理,即虛擬機採用CAS配上失敗重試的方式保證更新操做的原子性。2、使用本地線程分配緩衝區(TLAB),哪一個線程要分配內存,就在哪一個線程的本地緩衝區進行分配,只有緩衝區用完了,在分配新的緩衝區的時候才須要同步鎖定。
賦初始值:保證對象的實例字段不賦初始值就能夠直接使用,能夠直接訪問這些字段的初始值。
虛擬機對對象設置:虛擬機會將一些必要信息保存在對象頭中,如:這個對象是哪一個類的實例,如何才能找到類的元數據信息,對象的哈希碼等等。
執行構造函數:此時站在虛擬機角度看對象已經建立好了,可是此時對象中字段仍是默認零值,須要執行構造函數,按照設計意圖構造好。