7.堆(線程共享)


一、堆的核心概述

			JVM啟動時,被創建,每一個JVM實例,都對應這一個堆空間,所有的線程共享  java堆 

每個線程占一小塊(TLAB),線程私有的,並發性更好一些

棧里存放的是 s1實例在 堆里的 地址

1.1、堆空間細分、內存細節

		現代垃圾收集器大部分都基於分代收集理論設計的 

1.1.1、jdk7以級之前 堆內存 邏輯上分為 三部分:新生區、養老區、永久區

1.1.2、jdk8之后 堆內存 邏輯上 分為三部分:新生區、養老區、元空間

1.1.3、同名都可叫

二、設置堆內存的大小與OOM

java堆區用於存儲java對象的實例,那么堆的大小在JVM啟動的時候就已經確定好了,大家可以通過選項設置“ -Xms”和“ - Xmx”來進行設置
------->“ -Xms” 表示 堆的 起始內存:“ -Xms:起始內存”
------->“ -Xmx” 表示 堆的 最大內存:“ -Xmx:最大內存”
一旦堆區的大小超過了 最大內存 ,那么就會報 OOM錯誤 ,OutOfMemoryError異常
**默認的情況:
初識內存大小:物理內存大小的/64
最大內存大小:物理內存大小的/4 **

如何取查看設置的參數:

			方式一:cmd -》jps-》jstat -gc +進程id
			方式二:-XX:+PrintGCDetails

三、年輕代與老年代

	幾乎所有的java對象都是在 年輕代的 eden 區被 new 出來的(eden放的下的話)
	絕大多數java對象的銷毀 都是在 新生代的 進行了(例如局部變量)

java堆 也可以分為 年輕代和老年代

年輕代 還可以分為 伊甸園區(Eden)和 幸存者0區(survivor 0)或者幸存者1區(survivor 1)
		幸存者零區和 幸存者一區 只能有一個 被使用

設置 年輕代與老年代 的 堆內存比例

設置 Eden 和 Survivor 的一個 比例 :
-XX:SurvivorRatio=8 , 8:1:1

四、圖解、描述對象分配過程

圖:

對象分配過程:

			1.new 的對象先放 到 eden 區,此區有大小限制
			2.當eden區填滿的時候,程序又需要創建對象,JVM的垃圾回收器將對eden區的垃圾進行回收(Minor GC),將 eden區 不在被引用的對象 進行銷毀,再加載新的對象到 eden區
			3.然后 將 eden 區 剩下的對象 移動到 幸存者零區(Survivor 0)
			4.如果伊甸園區在滿,再次觸發垃圾回收,一並回收三個區的垃圾,此時,上次幸存下來的在 幸存者零區(Survivor 0)的,如果沒有被回收,就會被 移動到 幸存者一區(Survivor 1),Eden區不是垃圾的對象也會被放到 幸存者一區(Survivor 1)
			5.如果再次經歷垃圾回收,此時,會重新放回 幸存者 零區(Survivor 0),接着再去幸存者一區(Survivor 1)......循環 0  1
			6.啥時候去養老區呢??可以設置次數。默認是 15次(被移動了15次的對象,有個計數器)
					-XX:MaxTenuringThreshold=<N>進行設置
			7.在養老區比較悠閑,當養老區不足時,觸發:Major GC ,進行養老區的內存清理
			8.若清理完 還不夠,就會產生OOM(OutOfMemoryError)異常

4.1、總結:

1 針對幸存者S0,S1區的總結,復制之后有交換,誰空誰是to
2 關於垃圾回收,頻繁的在新生區 收集,很少在養老區收集,幾乎不在 永久區/元空間 收集
3 程序流程圖

五、Minor GC(YGC)、Major GC(OGC)、Full GC

5.1、Major GC 和 Full GC 速度 要比 Minor GC 慢十倍以上,STW的時間更長,如果Major GC 后,還是不足,報OOM

JVM在進行GC時候,並非每次都對三個內存(新生代、老年代、元空間)進行一起回收,大部分的回收都是指的是新生代
針對HotSpot VM的實現,它里面的GC按照回收區域可以分為兩種:一種是部分收集(Partial GC)、一種是整堆收集(Full GC)
··········部分收集(Partial GC):不是完整的收集整個java 堆的垃圾。其中分為:
······················->新生代收集(YGC):只對新生代進行收集(eden/s0,s1)
······················->老年代收集(OGC):只對老年代進行收集
··································目前,只有 CMS GC會有單獨收集老年代的行為
··································很多時候 OGC 會和FULL GC 混淆使用 ,需要具體分析是老年代收集還是整體收集
·······················->混淆收集(Mixed GC):將新生代和部分老年代的垃圾收集
································目前,只有 G1 GC會有這種行為
··········整堆收集(Full GC):收集整個java堆內存的垃圾

-------補充:Minor GC(YGC) 會引發STW,暫停其它用戶線程,等待垃圾回收結束后,用戶線程才繼續執行

5.2、Full GC

六、堆空間分代思想

為什么要把java堆分代?不分代就不能正產工作嗎?

答:經研究,不同對象的生命周期不同,70-99%的對象都是臨時對象
新生代:有eden區兩個大小相同的幸存者區構成
老年代:存放新生代經理多次GC還存活的
分代的唯一理由 就是優化GC的性能

七、內存分配策略

針對不同年齡的對象分配原則如下:

1>優先分配到Eden區
2>大對象直接分配到老年代(盡量避免程序中出現超大的對象)
3>長期存活的對象分配到老年代
4>對象年齡的判斷(如果幸存者區中相同年齡的所有對象大小的總和大於幸存者區空間的一半,年齡大於或者等於該年齡的對象可以直接進入老年代,無需等到 年齡到達 Max的值)

八、為對象分配內存:TLAB

8.1、為什么要有TLAB(Thread Local Allocation Buffer)

		1.堆區線程共享的,任何線程都可以訪問到堆區的共享區
		2.由於對象實例的創建在JVM中非常頻繁,因此多個線程操作共享數據是線程不安全的
		3.為了避免多個線程同時操作同一地址,需要使用加鎖的機制,影響分配速度

8.2、什么是TLAB?

		1.從內存模型而不是垃圾收集的角度,對Eden區進行划分,JVM為每一個線程單獨分配一個私有的區域,它包含在Eden空間內
		2.多個線程同時分配內存,使用TLAB可以避免一系列的非線程安全得問題,同時還能增加內存分配的吞吐量,因此我們將這種方式叫做 快速分配策略

8.3、TLAB空間僅為Eden區的1%,空間很小

TLAB機制是首選,空間用盡了之后,JVM嘗試加鎖機制來保證操作的原子性

8.4、對象分配過程TLAB程序流程圖

		優先分配到TLAB區,TLAB在屬於Eden區的私有區域,
		如果內存不夠的話,直接分配到Eden的其他區域

九、小結堆空間的參數設置

測試堆空間常用的JVM參數


十、堆是分配對象存儲的唯一選擇嗎?

堆中分配是正常的,但是有特殊情況:就會被在棧上分配,從而提升性能(逃逸分析)

	逃逸:脫離了原有的范圍,如果逃逸出了方法,就會在堆上分配,否則,就在棧上分配,
			這也是常見的堆外存儲技術

沒有發生逃逸,則可以分配到棧上

逃逸分析概述

圖解

綠色區發生了逃逸,因為將sb返回了,這個對象可能被別的方法修改堆上分配
黃色區沒有發生逃逸,sb只在內部用,返回的是sb的字符串,棧上分配
實際就是說 在方法里new 對象 出沒出方法,出了方法就是逃逸了,沒出就是沒逃逸

不逃逸 之 代碼優化

1、棧上分配
		方法內new  void,自產自銷
2、同步省略

3、分離對象或標量替換

**標量替換:將一個結構體(對象)拆解后 分配在棧上

總結:先否定,在否定之前的否定

對象的分配都是分配在堆上


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM