解讀Jvm的內(nèi)存結(jié)構(gòu)與GC及jvm參數(shù)調(diào)優(yōu)
一、JVM 內(nèi)存結(jié)構(gòu)
1、對象主要存放在堆內(nèi)存中;方法和屬性主要存放在棧內(nèi)存中。
2、棧是運行時單位,用來解決程序運行時的問題,堆是存儲單位,解決數(shù)據(jù)存儲的問題。
3、堆伴隨著JVM的啟動而創(chuàng)建。
結(jié)構(gòu)圖

1、類加載子系統(tǒng)
負責從文件系統(tǒng)或者網(wǎng)絡(luò)加載Class信息,加載的信息存放在一塊稱之方法區(qū)的內(nèi)存空間。
2、方法區(qū)(method)
存放類、常量、常量池信息、包括字符串字面量和數(shù)字常量等, 只要是 static 關(guān)鍵字修飾的都在方法區(qū)。
方法區(qū)的數(shù)據(jù)是所有線程共享的。
3、堆(heap)
幾乎所有的對象實例都存放到Java堆中,堆空間是所有線程共享,比如new 出來的對象。
在堆中的分配的內(nèi)存,由java虛擬機自動垃圾回收器來管理。
其中堆又分為了新生代和老年代和永久代 (jdk 1.8后取消永久代增加元空間)。
優(yōu)點:是可以動態(tài)地分配內(nèi)存大小,垃圾收集器會自動收走這些不再使用的數(shù)據(jù)。
缺點:由于要在運行時動態(tài)分配內(nèi)存,存取速度較慢
4、棧(stack)
在多線程環(huán)境下,每個線程擁有一個棧和一個程序計數(shù)器,是線程私有的資源。
方法和屬性主要存放在棧內(nèi)存中。
java中的所有引用都在棧中,并指向堆。
引用是創(chuàng)建對象的時候創(chuàng)建的(new ),第二次創(chuàng)建發(fā)現(xiàn)棧中如果有該對象的引用就使用原引用,不會新建
優(yōu)點:存取速度比堆要快,僅次于直接位于CPU中的寄存器,
缺點:存在棧中的數(shù)據(jù)大小與生存期必須是確定的,缺乏靈活性,生存期就是他與對象的關(guān)聯(lián)的時間
5、本地方法棧
用于本地方法調(diào)用。Java虛擬機允許Java直接調(diào)用本地方法(通過使用C語言寫)
6、pc寄存器(了解即可)
PC(Program Couneter)寄存器也是每個線程私有的空間,
Java虛擬機會為每個線程創(chuàng)建PC寄存器,在任意時刻,
一個Java線程總是在執(zhí)行一個方法,這個方法稱為當前方法,
如果當前方法不是本地方法,PC寄存器總會執(zhí)行當前正在被執(zhí)行的指令,
如果是本地方法,則PC寄存器值為Underfined,
寄存器存放如果當前執(zhí)行環(huán)境指針、程序技術(shù)器、操作棧指針、計算的變量指針等信息。
7、執(zhí)行引擎
虛擬機核心的組件就是執(zhí)行引擎,它負責執(zhí)行虛擬機的字節(jié)碼,一般戶先進行編譯成機器碼后執(zhí)行。
8、垃圾收集器
垃圾收集系統(tǒng)是Java的核心,也是不可少的,Java有一套自己進行垃圾清理的機制,開發(fā)人員無需手工清理
二、堆–> 新生代/新生代/永久代

1、新生代
主要是用來存放新生的對象。一般占據(jù)堆的1/3空間。
由于頻繁創(chuàng)建對象,所以新生代會頻繁觸發(fā)MinorGC進行垃圾回收。
新生代又分為 Eden區(qū)、ServivorFrom、ServivorTo三個區(qū)。
Eden :Java新對象的出生地(如果新創(chuàng)建的對象占用內(nèi)存很大,則直接分配到老年代)。當Eden區(qū)內(nèi)存不夠的時候就會觸發(fā)MinorGC,對新生代區(qū)進行一次垃圾回收
ServivorTo :保留了一次MinorGC過程中的幸存者。
ServivorFrom :上一次GC的幸存者,作為這一次GC的被掃描者。
MinorGC 是采用復(fù)制算法來清理垃圾
2、老年代
主要是用來存放頻繁使用的對象。一般占據(jù)堆的2/3空間。
老年代的對象比較穩(wěn)定,所以MajorGC不會頻繁執(zhí)行。
在進行MajorGC前一般都先進行了一次MinorGC,使得有新生代的對象晉身入老年代,導致空間不夠用時才觸發(fā)。
當無法找到足夠大的連續(xù)空間分配給新創(chuàng)建的較大對象時也會提前觸發(fā)一次MajorGC進行垃圾回收騰出空間。
MajorGC 是采用標記清除算法來清理垃圾
2.1、永久代(jdk1.7前)
指內(nèi)存的永久保存區(qū)域,主要存放Class和Meta(元數(shù)據(jù))的信息,
Class在被加載的時候被放入永久區(qū)域. 它和存放實例的區(qū)域不同
GC不會在主程序運行期對永久區(qū)域進行清理,
永久代的區(qū)域會隨著加載的Class的增多而脹滿,最終拋出OOM異常。
在Java8中,永久代已經(jīng)被移除,被一個稱為“元數(shù)據(jù)區(qū)”(元空間)的區(qū)域所取代。
2.2、元空間(jdk1.8后)
元空間的本質(zhì)和永久代類似,都是對JVM規(guī)范中方法區(qū)的實現(xiàn)。
不過元空間與永久代之間最大的區(qū)別在于:元空間并不在虛擬機中,而是使用本地內(nèi)存。
默認情況下,元空間的大小僅受本地內(nèi)存限制。
類的元數(shù)據(jù)放入 native memory, 字符串池和類的靜態(tài)變量放入java堆中. 這樣可以加載多少類的元數(shù)據(jù)就不再由MaxPermSize控制, 而由系統(tǒng)的實際可用空間來控制.
三、垃圾回收算法
垃圾回收機制采用算法一覽圖

1、引用算法
使用于新生代,新創(chuàng)建的對象,默認引用值為15
當引用值為 0時,該對象被垃圾回收機制回收
如果有地方引用,可達狀態(tài),則加1, 如果沒有地方引用,不可達狀態(tài),則減1
引用說明,new對象的時候創(chuàng)建,在棧中,并指向堆中的對象
2、復(fù)制算法(MinorGC)
使用于新生代ds0,ds1中,引用算法的引用值到達了一定高度次數(shù),就會晉升到ds0,ds1中
新的頻繁使用的對象都會在一個區(qū)內(nèi),要么在ds0,要么在ds1,每次只會有一個區(qū)會有數(shù)據(jù)
假如現(xiàn)在數(shù)據(jù)在ds0中,如果ds0有存活的對象,把存活的對象賦值到ds1,然后清空ds0,新的頻繁使用對象也會晉升在ds1,假如現(xiàn)在數(shù)據(jù)在ds1中,則反之
3、 標記清除算法(MajorGC)
標記清除算法
首先掃描一次所有老年代,標記出存活的對象,然后回收沒有標記的對象。
- MajorGC的耗時比較長,因為要掃描再回收。
- MajorGC會產(chǎn)生內(nèi)存碎片,為了減少內(nèi)存損耗,我們一般需要進行合并或者標記出來方便下次直接分配。----------- 當老年代也滿了裝不下的時候,就會拋出OOM(Out of Memory)異常。
- 標記壓縮法在標記清除基礎(chǔ)之上做了優(yōu)化,把存活的對象壓縮到內(nèi)存一端,而后進行垃圾清理。(java中老年代使用的就是標記壓縮法)

4、 java 手動回收GC
----Java技術(shù)使用finalize()方法在垃圾收集器將對象從內(nèi)存中清除出去前,做必要的清理工作
----把對象賦值為null 在 System.gc(); 即可回收,但不保證100%
public class JVMDemo {
public static void main(String[] args) {
// 創(chuàng)建了堆內(nèi)存
JVMDemo05 jvmDemo05 = new JVMDemo05();
// 賦值為空后去除該對象的引用
//jvmDemo05 = null;
//手動垃圾回收,只有所有線程都無引用的對象才會會GC回收
System.gc();
}
/**
* obj 方法,垃圾回收前觸發(fā)
*/
protected void finalize() throws Throwable {
System.out.println("gc在回收對象...");
}
}四、JVM 參數(shù)調(diào)優(yōu)
堆的常用配置參數(shù)參考
-XX:+PrintGC每次觸發(fā)GC的時候打印相關(guān)日志-XX:+UseSerialGC串行回收-XX:+PrintGCDetails更詳細的GC日志-Xms堆初始值-Xmx堆最大可用值-Xmn新生代堆最大可用值-XX:SurvivorRatio用來設(shè)置新生代中eden空間和from/to空間的比例.含以-XX:SurvivorRatio=eden/from=den/to
圖文方式

1、設(shè)置最大堆內(nèi)存
----- 1、堆內(nèi)存默認大小4G,我們可以根據(jù)程序大小來指定,默認內(nèi)存足夠的情況下不會觸發(fā)垃圾回收機制
----- 2、初始的堆大小與最大堆大小設(shè)置為相等,減少垃圾回收次數(shù),如果內(nèi)存一旦達到初始值,就會觸發(fā)垃圾回收機制,頻繁的觸發(fā)垃圾回收機制影響線程及性能問題
參數(shù):
-Xms堆初始值-Xmx堆最大可用值
-Xms512m -Xmx512m -XX:+PrintGCDetails -XX:+UseSerialGC -XX:+PrintCommandLineFlags
2、設(shè)置新生代與老年代優(yōu)化參數(shù)
-Xms堆初始值-Xmx堆最大可用值-Xmn新生代大小,一般設(shè)為整個堆的1/3到1/4左右-XX:SurvivorRatio設(shè)置新生代中eden區(qū)和from/to空間的比例關(guān)系n/1
參數(shù):
-Xms512m -Xmx512m -Xmn170m -XX:SurvivorRatio=8 -XX:+PrintGCDetails -XX:+UseSerialGC
3、堆內(nèi)存溢出處理
錯誤原因
java.lang.OutOfMemoryError Java heap space 堆內(nèi)存溢出
解決辦法
設(shè)置堆內(nèi)存大小,根據(jù)實際大小設(shè)置
-Xms堆初始值-Xmx堆最大可用值-XX:+HeapDumpOnOutOfMemoryError內(nèi)存溢出拋出儲存快照
示例:
-Xms512m –Xmx512m -XX:+HeapDumpOnOutOfMemoryError
4、棧內(nèi)存溢出處理
錯誤原因
棧溢出 產(chǎn)生于遞歸調(diào)用,循環(huán)遍歷是不會的,但是循環(huán)方法里面產(chǎn)生遞歸調(diào)用, 也會發(fā)生棧溢出。
java.lang.StackOverflowError 棧內(nèi)存溢出
解決辦法
設(shè)置線程最大調(diào)用深度
-Xss5m 設(shè)置最大調(diào)用深度,默認為1M大小 示例: -Xms512m –Xmx512m -Xss5m -XX:+HeapDumpOnOutOfMemoryError
5、Tomcat內(nèi)存溢出
在catalina.sh 修改JVM堆內(nèi)存大小
JAVA_OPTS="-server -Xms800m -Xmx800m -XX:PermSize=256m -XX:MaxPermSize=512m -XX:MaxNewSize=512m"
五、 JVM 參數(shù)調(diào)優(yōu)二( GC- 垃圾回收器)
1、串行回收(Serial Collector)
------ 單線程執(zhí)行回收操作,回收期間暫停所有應(yīng)用線程的執(zhí)行,client模式下的默認回收器,
jvm參數(shù)
-XX:+UseSerialGC 設(shè)置為串行回收
詳細參數(shù)
-XX:+PrintGCDetails -Xmx512M -Xms512M -XX:+HeapDumpOnOutOfMemoryError -XX:+UseSerialGC -XX:PermSize=32M
2、并行回收(ParallelGC)
------ ParNew (新生代 )并行回收器在串行回收器基礎(chǔ)上做了改進,他可以使用多個線程同時進行垃圾回收,對于計算能力強的計算機而言,可以有效的縮短垃圾回收所需的實際時間。
------ ParallelOldGC (老年代) 回收器也是一種多線程的回收器,和新生代的ParallelGC回收器一樣,也是一種關(guān)往吞吐量的回收器,他使用了標記壓縮算法進行實現(xiàn)。
jvm參數(shù)
----ParNew設(shè)置(新生代) XX:+UseParNewGC 設(shè)置為并行回收 XX:ParaleiGCThreads 設(shè)置ParNew回收器工作時的線程數(shù)量,建議指定為電腦核數(shù)*2 ----ParallelOldGC設(shè)置(老年代) -XX:+UseParallelOldGC 設(shè)置為并行回收 -XX:+ParallelCThread 設(shè)置垃圾收集時的線程教量, 建議指定為電腦核數(shù)*2
詳細參數(shù)(UseParNewGC)
-XX:+PrintGCDetails -Xmx512M –Xms512M -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParNewGC -XX:PermSize=32M
詳細參數(shù)(UseParallelGC)
-XX:+PrintGCDetails -Xmx512M -Xms256M -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:ParallelGCThreads=8 -XX:PermSize=32M
G1回收器了解
G1回收器(Garbage-First)實在]dk1.7中提出的垃圾回收器,從長期目標來看是為了取代CMS回收器,G1回收器擁有獨特的垃圾回收策略,G1屬于分代垃圾回收器,區(qū)分新生代和老年代,依然有eden和from/to區(qū),它并不要求整個eden區(qū)或者新生代、老年代的空間都連續(xù),它使用了分區(qū)算法。
jvm調(diào)優(yōu)總結(jié)
1、初始堆值和最大堆內(nèi)存內(nèi)存越大,吞吐量就越高。
2、最好使用并行收集器,因為并行手機器速度比串行吞吐量高,速度快。
3、設(shè)置堆內(nèi)存新生代的比例和老年代的比例最好為1:2或者1:3。
4、減少GC對老年代的回收。
六、java 代碼中查看大小
public class JvmDemo01 {
public static void main(String[] args) throws InterruptedException {
byte[] b1 = new byte[1 * 1024 * 1024];
System.out.println("分配了1m");
jvmInfo();
Thread.sleep(3000);
byte[] b2 = new byte[4 * 1024 * 1024];
System.out.println("分配了4m");
Thread.sleep(3000);
jvmInfo();
}
static private String toM(long maxMemory) {
float num = (float) maxMemory / (1024 * 1024);
DecimalFormat df = new DecimalFormat("0.00");// 格式化小數(shù)
String s = df.format(num);// 返回的是String類型
return s;
}
static private void jvmInfo() {
// 最大內(nèi)存
long maxMemory = Runtime.getRuntime().maxMemory();
System.out.println("maxMemory:" + maxMemory + ",轉(zhuǎn)換為M:" + toM(maxMemory));
// 當前空閑內(nèi)存
long freeMemory = Runtime.getRuntime().freeMemory();
System.out.println("freeMemory:" +freeMemory+",轉(zhuǎn)換為M:"+toM(freeMemory));
// 已經(jīng)使用內(nèi)存
long totalMemory = Runtime.getRuntime().totalMemory();
System.out.println("totalMemory:" +totalMemory+",轉(zhuǎn)換為M"+toM(totalMemory));
}
}idea 設(shè)置參數(shù)
進入編輯找到對應(yīng)的啟動類,VM哪填寫JVM參數(shù)就好了


eclipse 設(shè)置參數(shù)

設(shè)置

啟動jar 項目設(shè)置參數(shù)
java -jar -Xms20m -Xmx20m xxxxx.jar
其他:壓力測試工具 JMeter,可測試接口吞吐量(每秒的并發(fā)量)
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
解決@CachePut設(shè)置的key值無法與@CacheValue的值匹配問題
這篇文章主要介紹了解決@CachePut設(shè)置的key的值無法與@CacheValue的值匹配問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12
一文解決System.in關(guān)閉后無法再繼續(xù)使用流的問題
這篇文章主要給大家介紹如何解決System.in關(guān)閉后無法再繼續(xù)使用流的問題,文中有詳細的解決方法和代碼示例,具有一定的參考價值,需要的朋友可以參考下2023-07-07
Mybatis之foreach標簽內(nèi)傳入list為空的問題
這篇文章主要介紹了Mybatis之foreach標簽內(nèi)傳入list為空的問題及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03
Java中實現(xiàn)定時任務(wù)的兩種方法舉例詳解
這篇文章主要給大家介紹了關(guān)于Java中實現(xiàn)定時任務(wù)的兩種方法,文中總結(jié)了各種實現(xiàn)方式的優(yōu)缺點,并給出了推薦的使用場景,通過代碼介紹的非常詳細,需要的朋友可以參考下2024-12-12
SpringBoot使用Nacos進行application.yml配置管理
Nacos是阿里巴巴開源的一個微服務(wù)配置管理和服務(wù)發(fā)現(xiàn)的解決方案,它提供了動態(tài)服務(wù)發(fā)現(xiàn)、配置管理和?服務(wù)管理平臺,Nacos使用Raft協(xié)議保證配置的一致性,同時支持多種配置?格式,如properties、yaml等,本文介紹了SpringBoot使用Nacos進行application.yml配置管理2024-12-12

