## 1. 內存結構
### 1.1 堆內存

* 永久代在jdk8之后被廢棄掉,取而代之的是元空間(MetaSpace)。
* 元空間與永久代類似,都是方法區的實現,最大的區別是:元空間不在jvm中,而使用本地內存
1. JVM內存劃分為堆內存和非堆內存,堆內存分為年輕代(Young Generation)、老年代(Old Generation),非堆內存就一個永久代(Permanent Generation)。
2. 年輕代又分為Eden和Survivor區。Survivor區由FromSpace和ToSpace組成。Eden區占大容量,Survivor兩個區占小容量,默認比例是8:1:1。
3. 堆內存用途:存放的是對象,垃圾收集器就是收集這些對象,然后根據GC算法回收。
**4. 非堆內存用途:永久代,也稱為方法區,存儲程序運行時長期存活的對象,比如類的元數據、方法、常量、屬性等**。
### 1.2 方法區
方法區是java jvm規范,具體實現 jdk 1.7 :永久代,jdk1.8:元空間
> 方法區是jvm規范的一個概念定義,每一個jvm都有自己的實現。存儲著**類信息**、**常量**、**靜態變量**
1. 在Java官方的HotSpot 虛擬機中,Java8版本以后,是用元空間來實現的方法區;在Java8之前的版本,則是用永久代實現的方法區;
2. 也就是說,“元空間” 和 “方法區”,一個是HotSpot 的具體實現技術,一個是JVM規范的抽象定義;
所以,并不能說“JVM的元空間是方法區”,但是可以說在Java8以后的HotSpot 中“元空間用來實現了方法區”。
這個元空間是使用本地內存(Native Memory)實現的,也就是說它的內存是不在虛擬機內的,所以可以理論上物理機器還有多個內存就可以分配,而不用再受限于JVM本身分配的內存了。
#### 1.2.1 常量池
* 常量池是方法區中的一部分,一個jvm只有一個常量池,線程共享
* 保存了在編譯期間就已經確定的數據。包括final常量值(局部常量、成員常量以及引用常量)和對象字面值;
* final常量:一切經過final關鍵字修飾的變量均為常量,final常量在定義時必須賦初值,否則編譯不通過;
* **Java 基本類型的包裝類的大部分都實現了常量池技術,即 Byte,Short,Integer,Long,Character,Boolean;前面 4 種包裝類默認創建了數值\[-128,127\] 的相應類型的緩存數據,Character創建了數值在\[0,127\]范圍的緩存數據,Boolean 直接返回True Or False。如果超出對應范圍仍然會去創建新的對象.**
**兩種浮點數類型的包裝類 Float,Double 并沒有實現常量池技術。**
```
Integer i1 = 33;
Integer i2 = 33;
System.out.println(i1 == i2);// 輸出 true
Integer i11 = 333;
Integer i22 = 333;
System.out.println(i11 == i22);// 輸出 false
Double i3 = 1.2;
Double i4 = 1.2;
System.out.println(i3 == i4);// 輸出 false
```
`Integer i1=40`;Java 在編譯的時候會直接將代碼封裝成 `Integer i1=Integer.valueOf(40)`;,從而使用常量池中的對象。
`Integer i1 = new Integer(40);`這種情況下會創建新的對象。
```
Integer i1 = 40;
Integer i2 = new Integer(40);
System.out.println(i1==i2);//輸出 false
```
```
Integer i1 = 40;
Integer i2 = 40;
Integer i3 = 0;
Integer i4 = new Integer(40);
Integer i5 = new Integer(40);
Integer i6 = new Integer(0);
System.out.println("i1=i2 " + (i1 == i2));//true
System.out.println("i1=i2+i3 " + (i1 == i2 + i3));//true
System.out.println("i1=i4 " + (i1 == i4));//false
System.out.println("i4=i5 " + (i4 == i5));//false
System.out.println("i4=i5+i6 " + (i4 == i5 + i6)); //true 拆箱實際上等同40==40
System.out.println("40=i5+i6 " + (40 == i5 + i6)); //true
```
語句 i4 == i5 + i6,因為+這個操作符不適用于 Integer 對象,首先 i5 和 i6 進行自動拆箱操作,進行數值相加,即 i4 == 40。然后 Integer 對象無法與數值進行直接比較,所以 i4 自動拆箱轉為 int 值 40,最終這條語句轉為 40 == 40 進行數值比較。
#### 1.2.2 字符串常量池
常量池存在于方法區當中。
* **String s1 = new String("abc");這句話創建了幾個字符串對象?**
**將創建 1 或 2 個字符串。如果池中已存在字符串常量“abc”,則只會在堆空間創建一個字符串常量“abc”。如果池中沒有字符串常量“abc”,那么它將首先在池中創建,然后在堆空間中創建,因此將創建總共 2 個字符串對象。**
```
String s1 = new String("abc");// 堆內存的地址值
String s2 = "abc";
System.out.println(s1 == s2);// 輸出 false,因為一個是堆內存,一個是常量池的內存,故兩者是不同的。
System.out.println(s1.equals(s2));// 輸出 true
```
## 2. 堆分代
1. 新生成的對象首先放到年輕代Eden區
2. 當Eden空間滿了,觸發Minor GC,存活下來的對象移動到Survivor0區
3. Survivor0區滿后觸發執行Minor GC,Survivor0區存活對象移動到Suvivor1區,這樣保證了一段時間內總有一個survivor區為空。經過多次Minor GC仍然存活的對象移動到老年代。
4. 老年代存儲長期存活的對象,占滿時會觸發Major GC=Full GC,GC期間會停止所有線程等待GC完成,所以對響應要求高的應用盡量減少發生Major GC,避免響應超時。
Minor GC : 清理年輕代
Major GC : 清理老年代
Full GC : 清理整個堆空間,包括年輕代和永久代
**所有GC都會停止應用所有線程**
## 3.為什么會堆內存溢出?
> **在年輕代中經過GC后還存活的對象會被復制到老年代中。當老年代空間不足時,JVM會對老年代進行完全的垃圾回收(Full GC)。如果GC后,還是無法存放從Survivor區復制過來的對象,就會出現OOM(Out of Memory)。**
**OOM(Out of Memory)異常常見有以下幾個原因:**
1)老年代內存不足:java.lang.OutOfMemoryError:Javaheapspace
2)永久代內存不足:java.lang.OutOfMemoryError:PermGenspace
3)代碼bug,占用內存無法及時回收。
OOM在這幾個內存區都有可能出現,實際遇到OOM時,能根據異常信息定位到哪個區的內存溢出。
## 4. 內存溢出排查方法
1 . 分析堆內存快照
可以通過添加個參數-XX:+HeapDumpOnOutMemoryError,讓虛擬機在出現內存溢出異常時Dump出當前的內存堆轉儲快照以便事后分析。
2. Java工具
**jstat分析各塊內存占用率和gc時間**

查詢結果表明:這臺服務器的新生代Eden區(E,表示Eden)使用了28.30%(最后)的空間,兩個Survivor區(S0、S1,表示Survivor0、Survivor1)分別是0和8.93%,老年代(O,表示Old)使用了87.33%。程序運行以來共發生Minor GC(YGC,表示Young GC)101次,總耗時1.961秒,發生Full GC(FGC,表示Full GC)7次,Full GC總耗時3.022秒,總的耗時(GCT,表示GC Time)為4.983秒。
jmap查看對象存活狀態,實例個數和占用內存
```
jmap -histo:live 1002
```

- 計算機網絡
- 基礎_01
- tcp/ip
- http轉https
- Let's Encrypt免費ssl證書(基于haproxy負載)
- what's the http?
- 網關
- 網絡IO
- http
- 工具
- Git
- 初始本地倉庫并上傳
- git保存密碼
- Gitflow
- maven
- 1.生命周期命令
- 聚合與繼承
- 插件管理
- assembly
- 資源管理插件
- 依賴范圍
- 分環境打包
- dependencyManagement
- 版本分類
- 找不到主類
- 無法加載主類
- 私服
- svn
- gradle
- 手動引入第三方jar包
- 打包exe文件
- Windows
- java
- 設計模式
- 七大原則
- 1.開閉原則
- 2. 里式替換原則
- 3. 依賴倒置原則
- 4. 單一職責原則
- 單例模式
- 工廠模式
- 簡單工廠
- 工廠方法模式
- 抽象工廠模式
- 觀察者模式
- 適配器模式
- 建造者模式
- 代理模式
- 適配器模式
- 命令模式
- json
- jackson
- poi
- excel
- easy-poi
- 規則
- 模板
- 合并單元格
- word
- 讀取
- java基礎
- 類路徑與jar
- 訪問控制權限
- 類加載
- 注解
- 異常處理
- String不可變
- 跨域
- transient關鍵字
- 二進制編碼
- 泛型1
- 與或非
- final詳解
- Java -jar
- 正則
- 讀取jar
- map
- map計算
- hashcode計算原理
- 枚舉
- 序列化
- URLClassLoader
- 環境變量和系統變量
- java高級
- java8
- 1.Lambda表達式和函數式接口
- 2.接口的默認方法和靜態方法
- 3.方法引用
- 4.重復注解
- 5.類型推斷
- 6.拓寬注解的應用場景
- java7-自動關閉資源機制
- 泛型
- stream
- 時區的正確理解
- StringJoiner字符串拼接
- 注解
- @RequestParam和@RequestBody的區別
- 多線程
- 概念
- 線程實現方法
- 守護線程
- 線程阻塞
- 筆試題
- 類加載
- FutureTask和Future
- 線程池
- 同步與異步
- 高效簡潔的代碼
- IO
- ThreadLocal
- IO
- NIO
- 圖片操作
- KeyTool生成證書
- 壓縮圖片
- restful
- 分布式session
- app保持session
- ClassLoader.getResources 能搜索到的資源路徑
- java開發規范
- jvm
- 高并發
- netty
- 多線程與多路復用
- 異步與事件驅動
- 五種IO模型
- copy on write
- code style
- 布隆過濾器
- 筆試
- 數據庫
- mybatis
- mybatis與springboot整合配置
- pagehelper
- 分頁數據重復問題
- Java與數據庫之間映射
- 攔截器
- 攔截器應用
- jvm
- 堆內存測試
- 線程棧
- 直接內存
- 內存結構
- 內存模型
- 垃圾回收
- 調優
- 符號引用
- 運行參數
- 方法區
- 分帶回收理論
- 快捷開發
- idea插件
- 注釋模板
- git
- pull沖突
- push沖突
- Excel處理
- 圖片處理
- 合并單元格
- easypoi
- 模板處理
- 響應式編程
- reactor
- reactor基礎
- jingyan
- 規范
- 數據庫