主頁 >  其他 > 【學習筆記】學習JVM,肝完這篇4w+字的文章識訓滿滿

【學習筆記】學習JVM,肝完這篇4w+字的文章識訓滿滿

2020-10-13 22:57:40 其他

大家好,我是oldou,這次文章介紹的是關于JVM的相關知識,學習來源還是觀看B站狂神的視頻學習、同時在網上查找了很多資料進行整理【文末有參考地址】,畢竟網上的學習資料很多很多,當然要好好的利用起來進行學習,文章內容可能整理得不是特別好的那種,但是看完絕對是有識訓的,如果文中有不對的地方還請各位指正,在此感激不盡,如果本文對你有所幫助,希望點贊支持一下哈,謝謝各位!【關于JVM的整體圖我整理好之后會發出一個鏈接】

目錄

  • 前言
  • JVM的初識(了解即可)
  • JVM的體系結構(掌握)
  • 類加載器(Class Loader)
    • 類加載器的作用
    • 類加載器的類別
    • 類加載器之間的關系
    • 類加載的程序圖
  • 雙親委派機制
    • 什么是雙親委派機制?
    • 原始碼分析
    • 委派機制的流程圖
    • 雙親委派機制的作用
  • 沙箱安全機制
    • 什么是沙箱?
    • Java中的安全模型
    • 組成沙箱的基本組件
  • Native關鍵字
  • 方法區【Method Area】
  • 理解一下堆疊【Stack】
  • 堆【Heap】
    • 什么是堆記憶體?
    • 堆記憶體的特點是什么?
    • New物件在堆中如何分配?
    • 堆和堆疊的區別
    • 堆記憶體的模型圖
    • 新生代
      • 為什么堆要分代呢?
      • 新生代的介紹
    • 新生代中的GC
      • 一個物件的一輩子
      • 有關年輕代的JVM引數
    • 老年代
    • 永久區【轉】
    • 元空間【轉】
  • OOM的分析
    • 使用JProfiler工具分析OOM原因
      • 安裝JProfiler工具
  • 垃圾回收GC的學習
    • 垃圾回識訓制的介紹
    • 垃圾回收的區域介紹
    • GC之參考計數演算法
    • 可達性分析演算法
    • GC之復制演算法Copying【重點】
    • GC之標記清除演算法(Mark-Sweep)
    • GC之標記壓縮演算法(Compacting)
    • GC之分代收集演算法【重點】
      • 分帶收集的概括
      • 新生代(復制演算法)
      • 老年代(標記-清除標記-壓縮)
      • 分代垃圾收集程序
  • GC演算法總結
  • JVM調優
    • 調優的目的
    • JDK常用JVM優化相關命令
    • JVM 常見引數
      • 記憶體設定
      • 記憶體設定經驗分享
    • 收集器設定
    • 垃圾回收統計資訊
    • 并行收集器設定
    • 收集器設定經驗分享
    • 總結
  • JVM面試題
  • 參考文章

前言

  • 請你談談對JVM的理解?Java8虛擬機和之前的變化更新有什么不一樣?
  • 什么是OOM?什么是堆疊溢位StackOverFlowError?怎么分析?
  • JVM的常用調優引數有哪些?
  • 記憶體快站如何抓取?怎么分析Dump檔案?
  • 談談你對JVM中的類加載器的認識

一問到這些問題,說實話沒學過JVM的同學一般都會大皺眉頭,然后默默地…
在這里插入圖片描述
不過也別灰心,遇見不會的就說明我們還有進步的空間,畢竟現在不會不代表我們以后不會,不會的我們可以去學,所以我們要努力不斷的學習新的技術、新的知識,因為人生本來就需要不斷的學習,下面我們開始進入正文吧,

JVM的初識(了解即可)

定義
JVM就是java虛擬機,它是一個虛構出來的計算機,可在實際的計算機上模擬各種計算機的功能,JVM有自己完善的硬體結構,例如處理器、堆疊和暫存器等,還具有相應的指令系統,

作用

  • JVM是java位元組碼執行的引擎,還能優化java位元組碼,使之轉化成效率更高的機器指令,
  • JVM中類的裝載是由類加載器和它的子類來實作的,類加載是java運行時一個重要的系統組件,負責在運行時查找和裝入類檔案的類,
  • 不同的平臺對應著不同的JVM,在執行位元組碼(class檔案)時,JVM負責將每一條要執行的位元組碼送給解釋器,解釋器再將其翻譯成特定平臺換將的機器指令并執行,這樣就實作了跨平臺運行,

作業原理
JVM在整個JDK中處于最底層,負責與作業系統的互動,作業系統裝入jvm是通過JDK中的java.exe來實作的,具體步驟如下:

  • a、創建JVM裝載環境和配置;
  • b、裝載jvm.dll;
  • c、初始化jvm.dll;
  • d、呼叫JNIEnv實體裝載并處理class類;
  • e、運行java程式

JVM的體系結構(掌握)

完整圖
在這里插入圖片描述

簡略圖
在這里插入圖片描述

當我們運行一個Java代碼的時候,會按照上圖步驟依次進行,下面簡單解釋上圖中的JVM運行時資料區域:

  • 1、程式計數器:指向當前執行緒正在執行的位元組碼的地址,行號,執行緒私有,無GC

  • 2、Java堆疊:存盤當前執行緒運行方法所需要的資料,指令,回傳地址,執行緒私有,無GC

  • 3、本地方法堆疊:與Java堆疊相同,不同的是它存的是本地方法的資料,

  • 4、方法區:存盤類資訊(欄位方法的位元組碼,部分方法的構造器),常量、靜態變數,JIT(即時編譯的資訊),執行緒共享,無GC,非堆區;(java.lang.OutOfMemoryError:PermGen space),

  • 5、堆-heap:存盤類實體,一個JVM實體只有一個堆記憶體,執行緒共享,需要GC,

  • 6、JNI【Java Native Interface】(Java本地方法介面)
    凡是帶了native關鍵字的方法就會進入到本地方法堆疊,其他的就是進入Java堆疊;

  • 7、 Navite Interface 本地介面
    本地介面的作用就是融合不同的編程語言為Java所用,它的初衷就是融合C/C++程式,Java剛誕生的時候是C/C++橫行的時候,那個時候想要立足就必須由呼叫C/C++的程式,于是就在記憶體中專門開辟了一塊區域處理標記為native的代碼,它的具體做法就是再Nativa Method Stack中登記native方法,再(Execution Engine)執行引擎執行的時候加載Native Libraies,
    目前該方法的使用越來越少了,除非是與硬體相關的應用,使用Java玩嵌入式等等,由于限制的異構領域間通信很發達,可以使用Socket通信等等,

  • 8、Native Method Stack 本地方法堆疊
    它的具體做法就是Native Method Stack中登記native方法,在(Execution Engine)執行引擎執行的時候加載Native Libraies【本地庫】,

各個版本之間的區別

  • JDK1.6以及之前:有永久代,字串常量池和運行時常量池都在方法區;
  • JDK1.7:有永久代,但已經逐步“去永久代”,字串常量池移到堆中,運行時常量池還在方法區中(永久帶);
  • JDK1.8之后:無永久代,字串常量池在堆中,運行時常量池在元空間;

類加載器(Class Loader)

類加載器的作用

類加載器,顧名思義就是用來加載類的,但是它的作用不僅僅用于加載類,因為對于任意一個類都需要加載它的類加載器和這個類本身以此確立它在Java虛擬機中的唯一性,而每一個類加載器都擁有一個獨立的類名稱空間,

說直白點,類加載器的作用就是比較兩個類是否“相等”,只有它們是由同一個類加載器加載的時候才有意義,對于同一個類,如果由不同的類加載器加載,那么它們必然不想等,(相等包括Class物件的equals方法、isAssignableFrom()方法、isInstance()方法回傳的結果,也包括用instanceof關鍵詞判斷的情況),

類加載器的類別

(1)BootstrapClassLoader(啟動類加載器,又名根加載器)
C++撰寫,用于加載Java核心庫 java.*(例如:java.lang.*),構造ExtClassLoader(擴展類加載器)AppClassLoader(系統類加載器),由于引導類加載器涉及到虛擬機本地實作細節,開發者無法直接獲取到啟動類加載器的參考,所以不允許程式員直接通過參考進行操作,

(2)ExtClassLoader(標準擴展類加載器)
該類加載器由Java撰寫,用于加載擴展類別庫,主要負責加載【jre/lib/ext】目錄下的一些擴展的jar,例如classpath中的jrejavax.*或者java.ext.dir指定位置的類,開發者可以直接使用擴展類加載器,

(3)AppClassLoader(系統類加載器)
Java撰寫,主要負責加載應用程式的主函式類,加載程式所在的目錄,例如user.dir所在的位置的class

(4)CustomClassLoader(用戶自定義類加載器)
Java撰寫,用戶自定義的類加載器,可加載指定路徑的class檔案,

開發者角度的類加載器位置
在這里插入圖片描述

  • 根類加載器,加載位于/jre/lib目錄中的或者被引數-Xbootclasspath所指定的目錄下的核心Java類別庫,此類加載器是Java虛擬機的一部分,使用native代碼(C++)撰寫,如圖所示,rt.jar這個jar包就是Bootstrap根類加載器負責加載的,其中包含了java各種核心的類如java.langjava.iojava.utiljava.sql

  • 擴展類加載器,加載位于/jre/lib/ext目錄中的或者java.ext.dirs系統變數所指定的目錄下的拓展類別庫,此加載器由sun.misc.Launcher$ExtClassLoader實作,

  • 系統類加載器,加載用戶路徑(ClassPath)上所指定的類別庫,此加載器由sun.misc.Launcher$AppClassLoader實作,

類加載器之間的關系

在這里插入圖片描述
圖中的層次關系,稱為類加載器的雙親委派模型,雙親委派模型要求除了頂層的根類加載器以外,其余的類加載器都應該有自己的父類加載器(一般不是以繼承實作,而是使用組合關系來復用父加載器的代碼),
如果一個類收到類加載請求,它首先請求父類加載器去加載這個類,只有當父類加載器無法完成加載時(其目錄搜索范圍內沒找到需要的類),子類加載器才會自己去加載

類加載的程序圖

在這里插入圖片描述

雙親委派機制

什么是雙親委派機制?

當某個類加載器需要加載某個.class檔案的時候,這個類加載器會首先將這個任務委托給它的上級類加載器(父級加載器),遞回這個操作,如果上級的類加載器沒有進行加載,那么這個類加載器才會自己去加載這個.class檔案,

原始碼分析

【我們在java.lang包中首先找到ClassLoader,然后打開ClassLoader類,Ctrl+F搜索loadClass方法,下面為該方法的原始碼】

public Class<?> loadClass(String name) throws ClassNotFoundException {
    return loadClass(name, false);
}
protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // 首先檢查這個class是否已經被加載過了
                Class<?> c = findLoadedClass(name);
        //如果 c==null就表示該class沒有被加載
                if (c == null) {
            long t0 = System.nanoTime();
            try {
                // 如果有父類的加載器就將該class委托父類加載器進行加載
                             if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    //如果父類的加載器為空,則說明遞回到bootStrapClassloader根加載器了
                                     //bootStrapClassloader比較特殊,無法通過get獲取
                                     c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {}
            //如果 c==null就表示該class在父加載器那邊沒有被加載
                      if (c == null) {
                //如果bootstrapClassLoader 仍然沒有加載過,則會一層一層的遞回回來,并且嘗試自己去加載這個class
                long t1 = System.nanoTime();
                c = findClass(name);
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

這里需要注意的一點就是long t0 = System.nanoTime();這個的原始碼public static native long nanoTime();中的native使用這個關鍵字宣告的方法表示告知JVM呼叫,該方法在外部定義,可能使用C/C++去實作了,這個關鍵字詳細的解釋可以百度去查一下,這里不做過多的介紹,下面使用流程圖來解釋一下原始碼中的流程,

委派機制的流程圖

【這里我畫的那張圖太大了不好截圖,參考了別人的圖,圖地址我放在文末了,后面我會將我自己畫的整個JVM的圖分享出來,】
在這里插入圖片描述
從上圖中我們就更容易理解了,當一個.class這樣的檔案要被加載時,不考慮我們自定義類加載器的話,首先就會在AppClassLoader中檢查是否加載過,如果有那就無需再加載了,如果沒有,那么會拿到父加載器,然后呼叫父加載器的loadClass方法,父類中同理會先檢查自己是否已經加載過,如果沒有再往上,注意這個程序,知道到達Bootstrap classLoader之前,都是沒有哪個加載器自己選擇加載的,如果父加載器無法加載,會下沉到子加載器去加載,一直到最底層,如果沒有任何加載器能加載,就會拋出ClassNotFoundException

雙親委派機制的作用

  • 保證資料安全,能夠防止重復加載同一個.class檔案,通過往父類加載器去委托,如果已經加載過了那么就不用再加載一遍;
  • 保證核心 .class不能被篡改,通過委托方式,不會去篡改核心 .class,即使篡改了也不會去加載,即使加載也不會是同一個 .class物件了,不同的加載器加載同一個 .class也不是同一個 Class物件,這樣保證了 Class執行安全,

沙箱安全機制

什么是沙箱?

Java安全模型的核心就是Java沙箱(sandbox),那么什么是沙箱呢?沙箱就是限制程式運行的環境,沙箱機制就是Java代碼限定JVM虛擬機特定的運行范圍中,并且嚴格限制代碼對本地系統資源的訪問,通過這樣的措施來保證對代碼的有效隔離,以防止對本地系統造成破壞,
沙箱主要限制系統資源的訪問,那系統資源包括哪些呢?----CPU、記憶體、檔案系統、網路,不同級別的沙箱對這些資源訪問的限制也會不一樣,但所有的Java程式運行都可以指定沙箱,可以定制安全策略,

Java中的安全模型

在Java中將執行程式分成本地代碼和遠程代碼兩種,本地代碼默認視為可信任的,而遠程代碼則被看作是不受信的,對于授信的本地代碼,可以訪問一切本地資源,而對于非授信的遠程代碼在早期的Java實作中,安全依賴于沙箱 (Sandbox) 機制,如下圖所示
在這里插入圖片描述
但如此嚴格的安全機制也給程式的功能擴展帶來障礙,比如當用戶希望遠程代碼訪問本地系統的檔案時候,就無法實作,因此在后續的Java1.1版本中,針對安全機制做了改進,增加了安全策略,允許用戶指定代碼對本地資源的訪問權限,如下圖所示
在這里插入圖片描述
在 Java1.2 版本中,再次改進了安全機制,增加了代碼簽名,不論本地代碼或是遠程代碼,都會按照用戶的安全策略設定,由類加載器加載到虛擬機中權限不同的運行空間,來實作差異化的代碼執行權限控制,如下圖所示
在這里插入圖片描述
當前最新的安全機制實作,則引入了域 (Domain) 的概念,虛擬機會把所有代碼加載到不同的系統域和應用域,系統域部分專門負責與關鍵資源進行互動,而各個應用域部分則通過系統域的部分代理來對各種需要的資源進行訪問,虛擬機中不同的受保護域 (Protected Domain),對應不一樣的權限 (Permission),存在于不同域中的類檔案就具有了當前域的全部權限,如下圖所示
在這里插入圖片描述
以上提到的都是基本的 Java 安全模型概念,在應用開發中還有一些關于安全的復雜用法,其中最常用到的 API 就是doPrivilegeddoPrivileged方法能夠使一段受信任代碼獲得更大的權限,甚至比呼叫它的應用程式還要多,可做到臨時訪問更多的資源,有時候這是非常必要的,可以應付一些特殊的應用場景,例如,應用程式可能無法直接訪問某些系統資源,但這樣的應用程式必須得到這些資源才能夠完成功能,

組成沙箱的基本組件

(1) 位元組碼校驗器(bytecode verifler:確保Java類檔案遵循Java語言規范,這樣可以幫助到Java程式實作記憶體保護,但并不是所有的類都會經過位元組碼校驗,比如核心類,

(2)類加載器(Class Loader:其中類加載器在以下三個方面對Java沙箱起作用

  • 它防止惡意代碼去干涉善意代碼;(使用了雙親委派機制)
  • 它守護了被信任的類別庫邊界;
  • 它將代碼歸入到保護域,確定了代碼可以進行哪些操作,
    虛擬機為不同的類加載器載入的類提供不同的命名空間,命名空間由一系列唯一的名稱組成,每一個被裝載的類將有一個名字,這個命名空間是由Java虛擬機為每一個類裝載器維護的,它們互相之間甚至不可見,

類裝載器采用的機制是雙親委派模式

  • 1、從最內層JVM自帶類加載器開始加載,外層惡意同名類得不到加載從而無法使用;
  • 2、由于嚴格通過包來區分了訪問域,外層惡意的類通過內置代碼也無法獲得權限訪問到內層類,破壞代碼就自然無法生效,

(3)存取控制器(access controller):存取控制器可以控制和訊API對作業系統的存取權限,而這個控制的策略設定可以由用于指定,
(4)安全管理器(security manager):是核心API和作業系統之間的主要介面,實作權限控制,比存取控制器優先級高,
(5)軟體安全包(security package):Java.security下的類和擴展包下的類,運行用戶為自己的應用增加新的安全也行,包括以下:

  • 安全提供者
  • 訊息摘要
  • 數字簽名
  • 加密
  • 鑒別

Native關鍵字

我們在原始碼中經常看見Native這個關鍵字,例如我們最常用的執行緒方法,

public static void main(String[] args) {

    new Thread(()->{

    },"myThread").start();
}

我們點進去這個start()方法,在該原始碼中我們發現這個start0()方法

public synchronized void start() {

    if (threadStatus != 0)
        throw new IllegalThreadStateException();

    group.add(this);

    boolean started = false;
    try {
        start0();
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {

        }
    }
}

我們找到start0()的定義發現竟然是private native void start0();這樣的,很驚奇,這個native干了啥呢?僅僅就是這樣宣告了一下就完事了,它到底是弄了什么操作呢?是不是有很多的問號,沒關系我們來學習一下,、

首先來看一下這個圖【注意標紅的地方】:
在這里插入圖片描述

  • 凡是使用了native關鍵字修飾的,就說明Java的作用范圍已經達不到了,這個時候就會去呼叫底層的C語言庫,

  • 凡是帶了native關鍵字的會進入本地方法堆疊:當類加載進來的時候,將堆、堆疊記憶體分配好之后就會進入到本地方法堆疊呼叫start0(),而本地方法堆疊里的東西Java范圍是作用不到的,那么本地方法堆疊就會呼叫本地方法介面【JNIJava Native Interface,作用寫在下面】,通過JNI加載本地方法庫中的方法去執行操作,

  • JNI的作用】:擴展Java的使用,它可以融合不同的編程語言為Java所用,最初是CC++,后續添加了其他語言,原因就是:Java剛誕生的時候C語言和C++那個時候超級流行,而想要立足的話就必須要有呼叫CC++的程式,于是就會在記憶體區域中專門開辟一個標記區域【Navicat Method Stack】用于登記native方法【只是登記而不用執行】,并且在最終執行的時候通過JNI加載本地方法庫中的方法,

注意:本地方法堆疊、本地方法介面還有本地方法庫的介紹在上面JVM體系結構部分已經解釋,請往上查看,

方法區【Method Area】

方法區是被所有執行緒共享,所有欄位和方法位元組碼,以及一些特殊方法,如建構式,介面代碼也在此定義,簡單說,所有定義的方法的資訊都保存在該區域,此區域屬于共享區間;

靜態變數【static】、常量【final】、類資訊(構造方法、介面定義)【Class】、運行時的常量池存在方法區中【常量池】,但是實體變數存在堆記憶體中,和方法區無關 ,

舉例子,根據代碼簡單畫一下記憶體圖【物件剛加載的時候是什么樣子的】:

public class Test {
    private int a;
    private String name="oldou";

    public static void main(String[] args) {
        Test test = new Test();
        test.a=1;
        System.out.println(test.a+"\t"+test.name);
    }
}

在這里插入圖片描述

理解一下堆疊【Stack】

注意:概念也許對你來說有些枯燥,但是當你真正沉下心看并且弄懂的時候,真的很有趣,下面的每句話基本上都需要理解,

堆疊stack)又名堆疊,它是一種資料結構【運算受限的線性表】,限定僅在表尾進行插入和洗掉操作的線性表,這一端被稱為堆疊頂,相對地,把另一端稱為堆疊底,向一個堆疊插入新元素又稱作進堆疊、入堆疊或壓堆疊,它是把新元素放到堆疊頂元素的上面,使之成為新的堆疊頂元素;從一個堆疊洗掉元素又稱作出堆疊或退堆疊,它是把堆疊頂元素洗掉掉,使其相鄰的元素成為新的堆疊頂元素,如下圖所示:
在這里插入圖片描述

  • 首先得搞清楚,堆疊的意思相當于存盤貨物或者供旅客住宿的地方,就是指資料暫時存盤的地方,因此才會由出堆疊、進堆疊的說法,

  • 堆疊作為一種資料結構,是一種只能在一端進行插入【push】和洗掉操作【pop】的特殊線性表,它按照先進后出的原則存盤資料,先進入的資料被壓入堆疊底,最后的資料在堆疊頂,需要讀資料的時候從堆疊頂開始彈出資料(最后一個資料被第一個讀出來),堆疊具有記憶作用,對堆疊的插入與洗掉操作中,不需要改變堆疊底指標,

  • 每一個執行的方法都會產生一個堆疊幀【以上就有兩個堆疊幀:方法A、方法B】,程式正在執行的方法一定在堆疊的頂部,方法執行完之后就會被彈出堆疊,直到全部執行完畢,堆疊的執行原理本來就是先進后出,后進先出,先進入的方法就會被后進的方法壓住【又名:壓堆疊】,堆疊就像一個桶一樣,如果堆疊堆滿了就會拋出錯誤 【StackOverflowError】,

  • 堆疊是允許在同一端進行插入和洗掉操作的特殊線性表,允許進行插入和洗掉操作的一端稱為堆疊頂(top),另一端為堆疊底(bottom);堆疊底固定,而堆疊頂浮動;堆疊中元素個數為零時稱為空堆疊,插入一般稱為進堆疊(PUSH),洗掉則稱為退堆疊(POP),堆疊也稱為先進后出表,與堆疊類似的佇列遵循FIFO原則【First Input First Output,先進先出】,

  • 這就是為什么我們程式中的main()方法先執行,結果最后結束的原因,因為main()方法入堆疊后被壓在堆疊低,上面的方法堆疊幀執行一個就POP一個最后main()出堆疊后才結束main()方法,

  • 堆疊記憶體主管著程式的運行,生命周期以及執行緒同步,執行緒結束后,堆疊記憶體就會釋放,所以對于堆疊來說,不存在垃圾回收問題,因為一旦執行緒結束了,堆疊就Over了,

堆疊中主要存放一些基本型別的變數(byte、short、int、long、float、double、boolean、char)、物件參考、實體的方法,

【注】關于堆疊的代碼實作之類的我就不放了,網上很多都有,后面再進行整理,

堆【Heap】

什么是堆記憶體?

堆記憶體是是Java記憶體中的一種,它的作用是用于存盤參考型別,當new實體化得到一個參考變數【物件或者陣列】的時候,java虛擬機會在堆記憶體中開辟一個不一定是連續的空間分配給該實體,根據零散的記憶體地址,實則是根據哈希演算法生成一長串數字指向該實體的物理地址,相當于門牌號起到標識作用,當參考丟失了,會被垃圾回識訓制回收,但不是立馬釋放堆記憶體,

堆是一種資料結構,它是存盤的單位,一個JVM只有一個堆記憶體,并且堆記憶體的大小是可以調節的,

  • 堆中存盤的全部是物件實體,每個物件都包含一個與之對應的class的資訊(class資訊存放在方法區),
  • jvm只有一個堆區(heap)被所有執行緒共享,堆中不存放基本型別和物件參考,只存放物件本身,幾乎所有的物件實體和陣列都在堆中分配,

堆記憶體的特點是什么?

堆記憶體的特點就是:

  • 堆記憶體可以看作是一個管道,FIFO【先進先出,后進后出】,
  • 堆可以動態地分配記憶體大小,生存期不需要事先告訴編譯器,缺點是由于要在運行時動態的分配記憶體,所以存取的速度慢,

New物件在堆中如何分配?

由Java虛擬機的自動垃圾回收器來進行管理

堆和堆疊的區別

  • 存放的東西不同:堆記憶體用于存放由new創建的物件或者陣列,堆疊記憶體用于物件參考和基本資料型別等等;

  • 存盤資料的原則不一樣:堆遵循FIFO原則【先進先出,后進后出】,而堆疊是先進后出,后進先出;

  • 當在一段代碼塊定義一個變數時,Java就在堆疊中為這個變數分配記憶體空間,當超過變數的作用域后,Java會自動釋放掉為該變數所分配的記憶體空間,該記憶體空間可以立即被另作他用,

  • 在堆中分配的記憶體,由Java虛擬機的自動垃圾回收器來管理,

堆記憶體的模型圖

在這里插入圖片描述

Java堆主要用于存放各種類的實體物件和陣列,它是垃圾收集器管理的主要區域,因此很多時候被稱為“GC堆”,在Java中堆被分為兩個區域:新生代和老年代,【注意這里不包括元空間(方法區),元空間原來叫永久代,JDK1.8之后改名為元空間】

下面就開始逐一的介紹一下新生代、老年代以及元空間,

新生代

為什么堆要分代呢?

為什么要給堆分代呢?當然咯,不分代也是可以的,只是分代的話可以優化GC性能,假如不分代的話,那我們創建的所有物件都放在一塊,當需要垃圾回收的時候我們需要去找哪些物件沒用,這樣就會對整個堆區域進行全面掃描,這樣耗性能啊,如果分帶的話,將新創建的物件放在某一個區域,當需要GC的時候就去掃描回收,多方便是不是,

新生代的介紹

新生代主要用于存盤新生的物件,一般需要占據堆的1/3的空間,由于頻繁創建物件,所以新生代會頻繁的觸發Minor GC進行垃圾回收,
新生代有分為Eden區【伊甸園區】、SurvivorFromSurvivorTo三個區域,下面依次介紹一下:

  • Enden區:是Java新物件的出生地(如果新創建的物件占用記憶體很大,則會被直接分配到老年代),當Eden區記憶體不夠的時候就會出發Minor GC,對新生代區進行一次垃圾回收,
  • SurvivorTo:保留了一次Minor GC程序中的幸存者;
  • SurvivorFrom:上一次GC的幸存者,作為這一次GC的被掃描者;

新生代中的GC

HotSpot JVM把年輕代分為了三部分:1個Eden區【伊甸園區】和2個Survivor區【幸存區,分別是From、To】,默認的比例為8:1,一般情況下,新創建的物件都會被分配到Eden區,這些物件再經過第一次的Minor GC后,如果還存活就會被轉移到Survivor區,物件在Survivor區中每熬過一次Minor GC,年齡就會被增長一次,當它們的年齡達到一次歲數的時候就會被移到老年代中,

因為新生代中的物件基本上都是朝生夕死(80%以上),所以在新生代中的垃圾回收演算法使用的是復制演算法復制演算法的基本思想就是將記憶體分為兩塊,每次只使用其中的一塊,當這一塊用完之后就將還或者的物件復制到另一塊上面,復制演算法并不會產生記憶體碎片,

在GC開始的時候,物件只會存在于Eden區和名為“From”的Survivor區,Survivor“To”是空的,緊接著進行GC,Eden區中所有存活的物件都會被復制到“To”,而在“From”區中,仍存活的物件會根據他們的年齡值來決定去向,年齡達到一定值(年齡閾值,可以通過-XX:MaxTenuringThreshold來設定)的物件會被移動到年老代中,沒有達到閾值的物件會被復制到“To”區域,經過這次GC后,Eden區和From區已經被清空,這個時候,“From”“To”會交換他們的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”,不管怎樣,都會保證名為ToSurvivor區域是空的,Minor GC會一直重復這樣的程序,直到“To”區被填滿,“To”區被填滿之后,會將所有物件移動到年老代中,
在這里插入圖片描述

一個物件的一輩子

我是一個普通的java物件,我出生在Eden區,在Eden區我還看到和我長的很像的小兄弟,我們在Eden區中玩了挺長時間,有一天Eden區中的人實在是太多了,我就被迫去了Survivor區的“From”區,自從去了Survivor區,我就開始漂了,有時候在Survivor的“From”區,有時候在Survivor的“To”區,居無定所,直到我18歲的時候,爸爸說我成人了,該去社會上闖闖了,于是我就去了老年代那邊,年老代里,人很多,并且年齡都挺大的,我在這里也認識了很多人,在老年代里,我生活了20年(每次GC加一歲),然后被回收,

【文章地址附在文末,搜了很多篇進行學習,感覺這個看著懂一些,描述得感覺蠻到位的…】

有關年輕代的JVM引數

  • (1)-XX:NewSize和-XX:MaxNewSize:用于設定年輕代的大小,建議設為整個堆大小的1/3或者1/4,兩個值設為一樣大,

  • (2)-XX:SurvivorRatio:用于設定Eden和其中一個Survivor的比值,這個值也比較重要,

  • (3)-XX:+PrintTenuringDistribution:這個引數用于顯示每次Minor GC時Survivor區中各個年齡段的物件的大小,

  • (4)-XX:InitialTenuringThreshol和-XX:MaxTenuringThreshold:用于設定晉升到老年代的物件年齡的最小值和最大值,每個物件在堅持過一次Minor GC之后,年齡就加1,

老年代

老年代用于存放新生代中經過多次垃圾回收仍然存活的物件,在老年代的物件都比較穩定,因此MajorGC不會頻繁執行,而在進行MajorGC之前一般都會進行一次MinorGC,使得新生代的物件晉入老年代,一般是空間不夠用時才觸發,當無法找到足夠大的連續空間分配給新創建的大物件的時候也會觸發一次MajorGC進行垃圾回收騰出空間,

  • MajorGC采用標記-清除演算法:首先掃描一次所有老年代,標記出存活的物件,然后回收沒有標記的物件,
  • MajorGC的耗時比較長,因為要先掃描然后再回收,
  • MajorGC會產生記憶體碎片,為了減少記憶體損耗,我們一般需要進行合并或者標記出來方便下次直接分配,
  • 當老年代也滿了裝不下的時候,就會拋出OOM(Out of Memory)例外,

下面來測驗一下,模擬一下這個OOM錯誤:
測驗代碼:

/**
 * 模擬OOM錯誤
 */
public class hello {
    public static void main(String[] args) {
        String str = "hello world";
        while (true){ //死回圈
            //通過不斷的產生新物件,然后堆記憶體溢位
            str = str + new Random().nextInt(888888888)
                    + new Random().nextInt(999999999);
        }
    }
}

運行報錯:在這里插入圖片描述
關于OOM的問題,后面再稍微詳細的說明,這里不做過多的介紹,

永久區【轉】

永久代,指的是記憶體的永久保存區域,主要用于存放Class和Meta(元資料)的資訊,Class在被加載的時候放入到永久區域,它和存放實體的區域不一樣,GC不會在主程式運行期間對永久代區域進行清理,所以也導致了永久代的區域會隨著加載的Class的增多而脹滿,最終拋出OOM例外,

在Java8中,永久代已經被移除,被一個稱為“元資料區”(元空間)的區域所取代,

元空間【轉】

其實,移除永久代的作業從JDK1.7就開始了,JDK1.7中,存盤在永久代的部分資料就已經轉移到了Java Heap或者是 Native Heap,但永久代仍存在于JDK1.7中,并沒完全移除,譬如符號參考(Symbols)轉移到了native heap;字面量(interned strings)轉移到了java heap;類的靜態變數(class statics)轉移到了java heap
我們還是通過以上模擬OOM錯誤分別在JDK1.6、JDK1.7、JDK1.8中運行:
JDK 1.6 的運行結果:
在這里插入圖片描述
JDK 1.7的運行結果:
在這里插入圖片描述
JDK 1.8的運行結果:
在這里插入圖片描述
從上述結果可以看出,JDK 1.6下,會出現“PermGen Space”的記憶體溢位,而在 JDK 1.7和 JDK 1.8 中,會出現堆記憶體溢位,并且 JDK 1.8中 PermSize 和 MaxPermGen 已經無效,因此,可以大致驗證 JDK 1.7 和 1.8 將字串常量由永久代轉移到堆中,并且 JDK 1.8 中已經不存在永久代的結論,現在我們看看元空間到底是一個什么東西?

結論:
元空間的本質和永久代類似,都是對JVM規范中方法區的實作,不過元空間與永久代之間最大的區別在于:元空間并不在虛擬機中,而是使用本地記憶體,因此,默認情況下,元空間的大小僅受本地記憶體限制,類的元資料放入 native memory、字串池和類的靜態變數放入java堆中.,這樣可以加載多少類的元資料就不再由MaxPermSize控制,而由系統的實際可用空間來控制,

元空間的大小可以通過以下引數來指定元空間的大小:

  • -XX:MetaspaceSize,初始空間大小,達到該值就會觸發垃圾收集進行型別卸載,同時GC會對該值進行調整:如果釋放了大量的空間,就適當降低該值;如果釋放了很少的空間,那么在不超過MaxMetaspaceSize時,適當提高該值,
  • -XX:MaxMetaspaceSize,最大空間,默認是沒有限制的,
    除了上面兩個指定大小的選項以外,還有兩個與 GC 相關的屬性:
    -XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空間容量的百分比,減少為分配空間所導致的垃圾收集
  • -XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空間容量的百分比,減少為釋放空間所導致的垃圾收集

【注:以上圖片文字摘自于:https://www.cnblogs.com/paddix/p/5309550.html】

OOM的分析

撰寫一段代碼,查看虛擬機視圖使用的最大記憶體和JVM的總記憶體

public class Demo01 {
    public static void main(String[] args) {
        //回傳虛擬機試圖使用的最大記憶體
        long maxMemory = Runtime.getRuntime().maxMemory(); //位元組
        //回傳JVM初始化的總記憶體
        long totalMemory = Runtime.getRuntime().totalMemory();

        System.out.println("maxMemory="+maxMemory+"位元組\t"+(maxMemory/(double)1024/1024)+"M");
        System.out.println("totalMemory="+totalMemory+"位元組\t"+(totalMemory/(double)1024/1024)+"M");
    }
}

輸出:
在這里插入圖片描述
然后我們再打開任務管理器查看一下我們的電腦記憶體:
在這里插入圖片描述

  • 我們將JVM虛擬機試圖使用的最大記憶體比上JVM初始化的記憶體約為【14.79:1】

  • JVM虛擬機試圖使用的最大記憶體比上系統的記憶體【1:4.5】

  • JVM初始化的記憶體比上系統的記憶體【1:66.77】

默認情況下,系統分配的總記憶體是電腦的1/4,而初始化的記憶體是1/64,

我們打開IDEA給我們剛剛的代碼進行虛擬機調參:【-Xms1024m -Xmx1024m -XX:+PrintGCDetails 】意思就是列印一些垃圾回收的資訊:
在這里插入圖片描述
再次運行:
在這里插入圖片描述
上圖中所顯示的可以對照上面我給出的堆記憶體模型圖查看,這個時候我們將年輕代的總空間+老年代的總空間然后轉換成M,305664K+699392K=1,005,056K=981.5M,仔細看看,我們的得出來的這個值竟然和虛擬機初始化總記憶體相等,那么問題就來了,我們后面還有一個元空間的記憶體,也有幾個M呢去哪兒了?這個時候回顧上面關于元空間的描述【元空間使用的是直接記憶體,是和堆分開的

如果以后遇到OOM的情況【堆記憶體滿了】,采取的措施:

  • 首先嘗試著將堆記憶體擴大看結果,假設我擴大了堆記憶體的空間走的原來的代碼還是報錯,就說明代碼有問題,因為這個時候就說明有垃圾代碼或者死回圈代碼在無限的占用堆空間;
  • 之后便是分析記憶體,看一下哪一個地方的代碼出了問題;

我們再去書寫一個類測驗一下OOM的問題

/**
 * 模擬OOM錯誤
 */
public class hello {
    public static void main(String[] args) {
        String str = "hello world";
        while (true){ //死回圈
            //通過不斷的追加,然后堆記憶體溢位
            str = str + new Random().nextInt(888888888)
                    +new Random().nextInt(999999999);
        }
    }
}

JVM調參【-Xms8m -Xmx8m -XX:+PrintGCDetails
運行輸出【這里由于我的電腦比較那個啥,所以將引數值調成了1M,無奈資訊還是輸出比較長,所以這里用的是狂神的圖】:
在這里插入圖片描述
首先前面四次是輕GC,第一次輕GC只花了一點點時間就將垃圾清理了,然后后面進行了三次輕GC清理垃圾,在第四次GC的時候發現伊甸園區和幸存區都滿了,這個時候就開始去走重GC去清理垃圾,走完重GC之后又可以走輕GC了,然后當輕GC清理不了的時候又去走重GC,當最后重GC清理不了的時候【注意PSYoungGen:0K–>0K …】,然后年輕代滿了,老年代滿了,元資料也滿了,這個時候就會報OOM【記憶體】的錯誤【OutOfMemoryError

使用JProfiler工具分析OOM原因

假如我們在一個專案中遇到了OOM故障,那么我們該如何去排除呢?如何定位出錯代碼?

  • 使用記憶體快照分析工具【像Eclipse中的MAT】【JProiler工具】
  • Dubug去一行行分析代碼【這個太麻煩了】

MAT、JProfiler的作用

  • 分析Dump記憶體檔案,快速定位記憶體泄露;
  • 獲得堆中的資料
  • 獲得大的物件

這里我們就介紹一下JProfiler工具的使用,

安裝JProfiler工具

話說IDEA可以集成插件的功能是真的香,這里我們使用IDEA去集成JProfiler插件,
直接打開IDEA—>Setting---->Plugins----->搜索JProfiler—>點擊安裝即可
在這里插入圖片描述
【注意】這里可能會有點小問題,就是IDEA搜不到這個插件,然后因為IDEA版本不一樣的問題,所以下圖中的選項也沒有,這里我的IDEA是2019的也沒有這個選項
在這里插入圖片描述
我的解決辦法就是更新一下Stable Releases,更新好了以后就會自動重啟IDEA,然后再去插件中搜JProfiler就有了,然后安裝即可,如果下圖中還有那個什么連接選項,可以將勾選項取消,我的IDEA沒有那個選項,具體原因可能這個IDEA是個閹割版吧…
在這里插入圖片描述
IDEA的JProfilter安裝好以后就多了一個下面這樣的東東:
在這里插入圖片描述
IDEA中弄好了這個插件以后就去安裝客戶端,這里需要去官網下載:https://www.ej-technologies.com/products/jprofiler/overview.html
在這里插入圖片描述
雙擊運行安裝即可,安裝位置可以自定義【安裝位置保證無中文】,需要注冊碼在百度上搜,
我這里安裝的是11最新版本的,下面給出注冊碼

A-tfbyKUM9Gw-KhGMbpYhS1#14246
S-QCM1I25qH1-CkLfdYOFs2#1018
L-GG5oEVjKQX-xEJjkR3QBb#1847
L-idEVpl1jvU-Ww3AnQGBUY#4148
S-p8q09PhrZp-ioZmzCnXlT#18231
L-Vy82rebM6e-nLYfOEykeP#34152
A-r8m8UInymG-S382j9ujs5#3265
A-iWZjln8l5O-QAG2CyKTeC#26123
L-MTGPt84xpw-06dzulmNLY#301110
L-fuoED44azj-OyQMvOutje#22275
L-J11-Everyone#speedzodiac-327a9wrs5dxvz#463a59
A-J11-Everyone#admin-3v7hg353d6idd5#9b4

這里要挨個的嘗試,安裝成功之后如下所示:在這里插入圖片描述
接下來就是將這個客戶端工具和IDEA結合起來:
在這里插入圖片描述
接下來進行測驗:

public class Demo03 {

    byte[] array = new byte[1*1024*1024];//1M
    
    public static void main(String[] args) {
        ArrayList<Demo03> list = new ArrayList<>();
        int count=0;
        try {
            while(true){
                list.add(new Demo03());
                count = count+1;
            }
        }catch (Exception e){ //模擬的應該是j ava.lang.OutOfMemoryError,這里卻拋出例外
            System.out.println("count :   "+count);
            e.printStackTrace();
        }
    }
}

運行一下:
在這里插入圖片描述
我們發現我們這個壓根捕獲不到,然后也不知道代碼哪里出了問題【其實體外咋捕獲到Error呢,所以我們這里只是模擬而已】

接下來我們要去Dump一下它的記憶體快照去分析一下,我們去這個Demo03中添加一些配置列印一下資訊,引數【-Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError】意思就是從堆里面Dump出一些檔案,條件就是On后面的,當出現OutOfMemoryError錯誤時就把檔案Dump下來,
在這里插入圖片描述
再次運行:在這里插入圖片描述
發現檔案被Dump出來了,名字叫做java_piD46224.hprof檔案,這個檔案的位置在哪呢?
選中該java檔案,右鍵選擇Show in Explorer,然后到專案的根目錄下,就可以看見了,

在這里插入圖片描述
我們雙擊打開:
在這里插入圖片描述
然后我們可以通過這個Diggest Objects大物件選項查看,我們看見下面的ArrayList竟然占了99%,那么就可以判斷這個ArrayList出問題了,
在這里插入圖片描述
然后這是定位到了哪個物件出錯了,接下來便是通過執行緒定位到哪一行的出錯了,
在這里插入圖片描述
解決了錯誤之后記得將生成的快照檔案洗掉掉,因為里面生成了很多檔案占用記憶體比較大,然后下main解釋一下輸入引數的意思:
-Xms: 設定初始化記憶體分配大小,默認1/164
-Xmx:設定最大分配記憶體,默認是1/4
-XX:+PrintGCDetails :列印GC的垃圾回收資訊
-XX:+HeapDumpOnOutOfMemoryError:當出現OutOfMemoryError錯誤時就從堆里面Dump出一個檔案,

后面我會詳細的介紹調優引數…

垃圾回收GC的學習

首先關于GC有這么一些題目【文章后面會整理題目+答案】:

  • JVM的記憶體模型和磁區,詳細到每個區放些什么東西?
  • 堆里面有哪些磁區?這些磁區的特點是什么?
  • GC的演算法有哪些?它們的作業原理是什么?說說它們的適用場景,
  • 輕GC和重GC分別是在什么時候發生的?

垃圾回識訓制的介紹

Java是一門面向物件編程的語言,而創建物件是常有的事,那么物件用完了之后該怎么處理呢?我們先做一下對比:

  • 在C++中,物件所占的記憶體是在程式運行結束之前一直被占用,同時釋放記憶體需要我們開發人員手動釋放,雖然作業量大,但是可控性高;【以下圖片來源于文末地址】
    在這里插入圖片描述
  • 在Java中,當沒有物件參考指向原先分配給物件的記憶體時,那么該記憶體就成為了“垃圾”,而JVM的一個系統級的執行緒會自動釋放該記憶體塊,雖然是自動化的,但是可控性比較差,有些甚至會出現記憶體溢位的情況;
    在這里插入圖片描述
    垃圾回收意味著程式不再需要的物件是“無用資訊”,這些“無用資訊”占用著系統的記憶體,因此需要將這些“無用資訊”進行清除從而回收記憶體,也就是說當一個物件不再被參考的時候,記憶體就會回收被它占用的空間,以便于給后來的新物件使用,

事實上,除了釋放沒有的物件,垃圾回收器也會清除記憶體記錄碎片,由于創建物件和垃圾回收器釋放丟棄物件所占的記憶體空間,記憶體會出現碎片,碎片是分配給物件的記憶體塊之間的空閑記憶體洞,碎片整理將所占用的堆記憶體移到堆的一端,JVM將整理出的記憶體分配給新的物件,

這么解釋理解了么,

垃圾回收的區域介紹

這里參考一下《深入理解Java虛擬機》一書中的某段話:

程式計數器、 虛擬機堆疊、 本地方法堆疊3個區域隨執行緒而生,隨執行緒而滅;堆疊中的堆疊幀隨著方法的進入和退出而有條不紊地執行著出堆疊和入堆疊操作, 每一個堆疊幀中分配多少記憶體基本上是在類結構確定下來時就已知的(盡管在運行期會由JIT編譯器
進行一些優化,但在本章基于概念模型的討論中,大體上可以認為是編譯期可知的),因此這幾個區域的記憶體分配和回收都具備確定性,在這幾個區域內就不需要過多考慮回收的問題,因為方法結束或者執行緒結束時,記憶體自然就跟隨著回收了, 而Java堆和方法區則不一樣,一個介面中的多個實作類需要的記憶體可能不一樣,一個方法中的多個分支需要的記憶體也可能不一樣,我們只有在程式處于運行期間時才能知道會創建哪些物件,這部分記憶體的分配和回收都是動態的,垃圾收集器所關注的是這部分記憶體

首先我們上面的JVM的體系結構介紹就可以明白,垃圾回收的區域主要就是尋找Java堆中的物件,并且將物件進行分類判斷,尋找出正在使用的物件和已經不會使用的物件,然后將不會使用的物件從堆上清除掉,而我們都知道,我們人腦是活的,但程式是死的,它咋指導哪些物件是需要清除的,哪些物件是不需要清除的呢?這就涉及到演算法了,對,就是垃圾回收的演算法,我們下面就進入演算法學習的主題吧!!

GC之參考計數演算法

概念:參考計數法,簡單的來說就是判斷物件的參考數量,
實作方式:給物件添加一個參考計數器,每當對物件進行參考時,該物件身上的計數器值就會加1,當參考失效后【也就是不再執行該物件的時候】,該物件身上的計數器值就會減1,若某一個物件的計數器的值為0,那么表示這個物件已經沒有人對它進行參考了,也就意味著是該物件時一個失效的垃圾物件,就會被垃圾回收器GC進行回收,如下所示【畫得不是很明確,但是結合文字理解起來應該行】
在這里插入圖片描述

問題:該演算法不能解決物件之間的回圈參考問題,所以雖然簡單,但是在當前的JVM中并沒有采用,
假設有A和B兩個物件之間互相參考,也就是說A物件中的一個屬性是B,B中的一個屬性時A,這種情況下由于他們的相互參考,從而是垃圾回識訓制無法識別它們到底該不該被回收,感覺像是耍無賴,相互呼叫一直沒辦法回收,然后我們都知道參考計數器也是需要占記憶體的,每個物件身上都弄一個計數器,當物件多了自然會影響性能,

可達性分析演算法

由于參考計數法的缺點,從而引入了可達性分析演算法,該演算法通過判斷物件的參考鏈(Reference Chain)是否可達來決定物件是否可以被回收,可達性分析演算法是從離散數學中的圖論引入的,程式把所有的參考關系看作一張圖,通過一系列的名為GC Roots的物件作為起始點,從這些節點開始向下搜索,搜索所走過的路徑稱為參考鏈(Reference Chain),當一個物件到GC Roots沒有任何參考鏈相連(就是從GC Roots到這個物件不可達)時,則證明此物件是不可用的,【看圖說話】
在這里插入圖片描述
在Java語言中,可作為GC Roots的物件包括下面幾種:

  • 虛擬機堆疊(堆疊幀中的本地變數表),中的參考的物件;
  • 方法區中類靜態屬性參考的物件;
  • 方法區中常量參考的物件;
  • 本地方法堆疊中JNI(本地方法介面)參考的物件;

GC之復制演算法Copying【重點】

首先,這點要記住的是:復制演算法是針對新生代的,本文的新生代中有所介紹,這里再詳細的說明一下,

  • 回顧:首先我們經過上文的學習知道新生代分為了一個Eden區【伊甸園區】和兩個Survivor區【幸存區,From區、To區】,【建議這里將上面新生代中的GC結合看】

  • 概念:復制演算法將記憶體按照容量劃分為了兩個大小相等的兩塊【就是幸存區的兩塊】,每次只使用其中的一塊記憶體,當這一塊的記憶體用完了,就將還活著的物件復制到另一塊上面,然后再把已經使用過的記憶體空間做一次清理,

  • 優點:每次對其中的一塊區域的記憶體進行回收,記憶體分配時就不用考慮記憶體碎片等復雜的問題,并且只要移動堆頂指標,按照順序分配記憶體即可,實作簡單,運行高效,

  • 缺點:這種演算法的代價就是將記憶體縮小為原來的一半,【因為兩個區域中一直保持有一個區域(To)為空】

  • 模型圖
    在這里插入圖片描述

  • 執行程序圖
    在這里插入圖片描述
    這里就用上圖結合新生代來解釋:
    在GC【垃圾回收】開始的時候,物件只會存在于Eden區和名為“From”Survivor區【因此Eden是產生新物件的地方】,而Survivor區“To”是空的【這里是重點,兩個區中To區一直保證是空的】,緊接著開始進行GC,Eden區中所有存活的物件都會被復制到“To”,而在“From”區中仍存活的物件會根據他們的年齡值來決定去向,年齡達到一定值(年齡閾值,默認是15,這里指的是經歷15次GC還存活的物件,這個閾值可以通過-XX:MaxTenuringThreshold來設定)的物件會被移動到年老代中,沒有達到閾值的物件會被復制到“To”區域,經過這次GC后,Eden區和From區已經被清空,這個時候,“From”“To”會交換他們的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”,不管怎樣,都會保證名為ToSurvivor區域是空的,Minor GC會一直重復這樣的程序,直到“To”區被填滿,“To”區被填滿之后,會將所有物件移動到老年代中,如下圖所示:
    在這里插入圖片描述

  • 其他知識
    現在的商業虛擬機都是采用復制演算法來回收新生代,并且有研究表明,新生代的98%的物件都是朝生夕死的,所以新生代中的記憶體區域劃分比例都不是按照1:1來劃分,HotSpot虛擬機默認是采用Eden:Survivor=8:1來劃分記憶體的,【關于HotSpot虛擬機,我們都是用的這個,不信的話Win+R打開運行執行CMD打開Windows命令視窗輸入java -version查看,如下所示】
    在這里插入圖片描述
    當然了,如果Survivor空間不夠用的時候,就會進行分配擔保(Handle Promotion)依賴一下老年代的空間,也就是假如Survivor的一塊空間無法存放上一次新生代經歷GC后存活下來的物件,那么這些物件就會通過分配擔保機制進入老年代

以上關于復制演算法的解釋懂了沒,沒懂的話就需要多看幾遍多理解一下哈,下面我們繼續介紹其他的演算法,

GC之標記清除演算法(Mark-Sweep)

標記清除演算法(Mark-Sweep),它是最基礎的一種垃圾回收【GC】演算法,它分為兩個步驟:

  • 第一步:標記(Mark),首先掃描記憶體區域中的物件,然后將記憶體區域中活著的物件和需要回收的物件進行標記;簡單的說就是找出垃圾在哪,如下圖所示:
    在這里插入圖片描述
    堆中的所有物件都會被掃描一次,以此確定那些需要被回收的物件位置,這個程序比較耗時,

  • 第二步:清除(Normal Deletion),垃圾回收器會把那些標記需要回收的物件全部清理掉,清理掉的垃圾就變成未使用的記憶體區域,等待被再次使用,但它存在一個很大的問題,那就是記憶體碎片,
    在這里插入圖片描述
    標記清除之后會產生大量不連續的記憶體碎片,空間碎片太多可能會導致以后在程式運行程序中需要分配較大物件時,無法找到足夠的連續記憶體而不得不提前觸發另一次垃圾收集動作,

標記清除法的優點:不需要額外的空間;
標記清除法的缺點:以上程序需要兩次掃描,嚴重浪費時間,同時會產生記憶體碎片,

【記憶體碎片】:指的是那些已經分配出去(能明確指出屬于哪個行程)卻不能被利用的空閑記憶體以小而不連續方式出現在不同的位置的記憶體,

GC之標記壓縮演算法(Compacting)

由于標記清除法中對標記的物件只是簡單的清除可能會導致記憶體碎片的問題,而記憶體碎片太多的話可能會導致在程式運行的程序中需要分配大物件而無法找到足夠的連續記憶體,因此不得不提前觸發一次垃圾回收動作,標記壓縮演算法和標記清除演算法很類似,最顯著的區別就是:標記清除演算法僅對不存活的物件進行處理,而剩下的活著的物件不做任何處理,從而造成記憶體碎片,而標記壓縮演算法不僅對不存活的物件進行處理,還對剩下的活著的物件進行整理,將記憶體區域掃描一遍,然后將活著的物件向一端移動進行整理,因此不會產生記憶體碎片,
在這里插入圖片描述
優點:相當于優化了標記清除演算法產生記憶體碎片的缺點;
缺點:多了一次掃描,還多了一次移動物件的成本,

GC之分代收集演算法【重點】

分帶收集的概括

分代收集演算法是一種比較智能的演算法,也是現在JVM使用最多的一種演算法,其實它本身并不是一個新的演算法,而是他會在具體的場景自動選擇以上三種演算法【這里三種演算法指的是復制、標記清除、標記壓縮】進行垃圾物件回收,針對不同的生命周期、不同大小的物件采用不同的垃圾回收策略,

為啥分代?【上面已經解釋過了】當然是為了增大垃圾收集的效率,所以JVM就將堆進行了分代:新生代、老年代,
在這里插入圖片描述

新生代(復制演算法)

所有新new出來的物件都會最新出現在新生代中,并且大多數物件的生命周期都是朝生夕死【研究表明這概率有98%】,只有少量的物件存活下來,因此新生代中采用的是復制演算法,發生在新生代中的垃圾回收被稱為Minor collections【Minor GC:輕GC】,而且上面的復制演算法中我也介紹了當Survivor的一塊空間無法存放上一次新生代經歷GC后存活下來的物件,那么這些存活物件就會通過分配擔保機制進入老年代,如果老年代也滿了就會觸發一次Full GC【重GC】,也就是新生代和老年代都進行回收,需要注意的是新生代發生的GC也叫Minor GC并且Minor GC發生的頻率是比較高的,不一定等Eden區滿了之后才會觸發,【現在回過頭去看我的模擬OOM錯誤哪個地方,增加虛擬機引數的輸出,Minor GC出現的次數是不是很多】,

老年代(標記-清除標記-壓縮)

老年代這里一般存放的都是一些生命周期比較長的物件,就像上面所說,在新生代中經歷了N次【默認是15次】的Minor GC后仍然存活的物件就會被放到老年代中,當然了,老年代中的記憶體比新生代大得多,大約是新生代的兩倍,當老年代記憶體滿了之后就會觸發一次Major GCFull GC:又名重GC),老年代中的物件存活時間比較長,因此出現Full GC的概率比較低,由于老年代中的物件存活率高,沒有額外的空間對它進行分配擔保,所以就使用標記-清除或者標記-壓縮演算法【又名:標記-整理】,

又重溫了一下新生代和老年代的東西,下面我們還是進入主題,關于分代收集演算法的分帶GC的程序,

分代垃圾收集程序

  • 第一步 所有new出來的物件都會最先分配到新生代區域中,兩個survivor區域初始化是為空的
    在這里插入圖片描述

  • 第二步,當eden區域滿了之后,就引發一次 Minor garbage collection(Minor GC:輕GC)
    在這里插入圖片描述

  • 第三步,在一次Minor GC后,將存活下來的物件就會被移動到S0survivor區域
    在這里插入圖片描述

  • 第四步,然后當Eden區域又填滿的時候,又會發生下一次的垃圾回收,存活的物件會被移動到survivor區域而未存活物件會被直接洗掉,但不同的是,在這次的垃圾回收中,存活物件和之前的survivor中的物件都會被移動到s1中,一旦所有物件都被移動到s1中,那么s0中的物件就會被清除,仔細觀察圖中的物件,數字表示經歷的垃圾收集的次數,目前我們已經有不同的年齡物件了,
    在這里插入圖片描述

  • 第五步,下一次垃圾回收的時候,又會重復上次的步驟,清除需要回收的物件,并且又切換一次survivor區域,所有存活的物件都被移動至s0,eden和s1區域被清除,
    在這里插入圖片描述

  • 第六步,重復以上步驟,并記錄物件的年齡,當有物件的年齡到達一定的閾值的時候,就將新生代中的物件移動到老年代中,在本例中,這個閾值為8,【默認是15】
    在這里插入圖片描述

  • 第七步,接下來垃圾收集器就會重復以上步驟,不斷的進行物件的清除和年代的移動
    在這里插入圖片描述

  • 最后,我們觀察上述程序可以發現,大部分的垃圾收集程序都是在新生代進行的,直到老年代中的記憶體不夠用了才會發起一次major GC(重GC),會進行標記和整理壓縮,
    在這里插入圖片描述

GC演算法總結

我們從各個角度比較一下復制演算法、標記清除演算法、標記壓縮演算法:

  • 記憶體效率:復制演算法>標記-清除演算法>標記-壓縮演算法(時間復雜度)
  • 記憶體整齊度:復制演算法=標記-壓縮演算法>標記演算法-清除
  • 記憶體利用率:標記-壓縮演算法=標記-清除演算法>復制演算法

那么問題就來了,難道就沒有一個最優的演算法么,emmmmmm,沒有最優的,只有最合適的,現在不是一般都使用分帶收集演算法嘛,感覺我了解到這個演算法以后,感覺確實挺優秀的,根據不同的生命周期、不同大小的物件采用不同的垃圾回收策略,挺高級的【個人覺得】,

JVM調優

調優的目的

  • 1.控制GC的行為
    GC是一個后臺處理,但是它也是會消耗系統性能的,因此經常會根據系統運行的程式的特性來更改GC行為,

  • 2.控制JVM堆疊大小
    一般來說,JVM在記憶體分配上不需要你修改,但是當你的程式新生代物件在某個時間段產生的比較多的時候,就需要控制新生代的堆大小,同時,還要需要控制總的JVM大小避免記憶體溢位,

  • 3.控制JVM執行緒的記憶體分配
    如果是多執行緒程式,產生執行緒和執行緒運行所消耗的記憶體也是可以控制的,需要通過一定時間的觀測后,配置最優結果,

JDK常用JVM優化相關命令

待更新…

JVM 常見引數

配置方式java [options] MainClass [arguments]
options - JVM啟動引數, 配置多個引數的時候,引數之間使用空格分隔,
引數命名: 常見為 -引數名
引數賦值: 常見為 -引數名=引數值 | -引數名:引數值

記憶體設定

  • -Xms:初始堆記憶體大小,JVM 啟動的時候,給定堆空間大小,
  • -Xmx:最大堆記憶體大小,JVM 運行程序中,如果初始堆空間不足的時候,最大可以擴展到多
    少,
  • -Xmn:設定新生代大小,整個堆大小=新生代大小+老年代大小+持久代大小,持久代一般固定大小為 64M,所以增大新生代后,將會減小老年代大小,此值對系統性能影響較大,Sun 官方推薦配置為整個堆的3/8
  • -Xss: 設定每個執行緒的 Java 堆疊大小,JDK5.0 以后每個執行緒 Java 堆疊大小為 1M,以前每個執行緒堆疊大小為 256K,根據應用的執行緒所需記憶體大小進行調整,在相同物理記憶體下,減小這個值能生成更多的執行緒,但是作業系統對一個行程內的執行緒數還是有限制的,不能無限生成,經驗值在3000~5000 左右,
  • -XX:NewSize=n:設定新生代大小
  • -XX:NewRatio=n:設定新生代和老年代的比值,如:為3的話就表示新生代與老年代比值為 1:3,年輕代占整個新生代+老年代和的 1/4
  • -XX:SurvivorRatio=n:新生代中 Eden 區與兩個 Survivor 區的比值,注意 Survivor 區有兩個,如:3,表示 Eden:Survivor=3:2,一個 Survivor 區占整個新生代的 1/5
  • -XX:MaxPermSize=n:設定持久代大小
  • -XX:MaxTenuringThreshold:設定垃圾最大年齡,如果設定為 0 的話,則新生代物件不經過 Survivor 區,直接進入老年代,對于老年代比較多的應用,可以提高效率,如果將此值設定為一個較大值,則年輕代物件會在 Survivor 區進行多次復制,這樣可以增加物件再新生代的存活時間,增加在新生代即被回收的概率,

記憶體設定經驗分享

JVM 中最大堆大小有三方面限制:相關作業系統的資料模型(32-bt 還是 64-bit)限制;
系統的可用虛擬記憶體限制;系統的可用物理記憶體限制,32 位系統 下,一般限制在 1.5G~2G;
64 為作業系統對記憶體無限制,

Tomcat 配置方式 : 撰寫catalina.bat|catalina.sh, 增加JAVA_OPTS引數設定 ,
windows和 linux 配置方式不同,
windows - set "JAVA_OPTS=%JAVA_OPTS%自定義引數 "
linux -JAVA_OPTS="$JAVA_OPTS 自定義引數 "

常見設定

  • -Xmx3550m -Xms3550m -Xmn2g -Xss128k適合開發程序的測驗應用,要求物理記憶體大于
    4G,

  • -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=160m -XX:MaxTenuringThreshold=0:適合高并發本地測驗使用,且大資料物件相對較多(如 IO 流)

環境: 16G 物理記憶體,高并發服務,重量級物件中等(執行緒池,連接池等),常用物件比例為 40%(運行程序中產生的物件 40%是生命周期較長的)
-Xmx10G -Xms10G -Xss1M -XX:NewRatio=3 -XX:SurvivorRatio=4 -XX:MaxPermSize=2048m -XX:MaxTenuringThreshold=5

收集器設定

收集器配置的時候,次收集器和全收集器必須匹配,具體匹配規則見下圖
在這里插入圖片描述

-XX:+UseSerialGC:設定串行收集器,年輕帶收集器, 次收集器

-XX:+UseParallelGC:設定并行收集器

-XX:+UseParNewGC:設定年輕代為并行收集,可與 CMS 收集同時使用,JDK5.0 以上,JVM會根據系統配置自行設定,所以無需再設定此值,

-XX:+UseParallelOldGC:設定并行年老代收集器,JDK6.0 支持對年老代并行收集,

-XX:+UseConcMarkSweepGC:設定年老代并發收集器,測驗中配置這個以后,-XX:NewRatio的配置失效,原因不明,所以,此時年輕代大小最好用-Xmn 設定,

-XX:+UseG1GC:設定 G1 收集器

垃圾回收統計資訊

類似日志的配置資訊,會有控制臺相關資訊輸出, 商業專案上線的時候,不允許使用,
一定使用loggc

-XX:+PrintGC
-XX:+Printetails
-XX:+PrintGCTimeStamps
-Xloggc:filename

并行收集器設定

  • -XX:ParallelGCThreads=n:設定并行收集器收集時最大執行緒數使用的 CPU 數,并行收集執行緒數,
  • -XX:MaxGCPauseMillis=n:設定并行收集最大暫停時間,單位毫秒,可以減少 STW 時間,
  • -XX:GCTimeRatio=n:設定垃圾回收時間占程式運行時間的百分比,公式為 1/(1+n)并發收集器設定
  • -XX:+CMSIncrementalMode:設定為增量模式,適用于單 CPU 情況,
  • -XX:+UseAdaptiveSizePolicy:設定此選項后,并行收集器會自動選擇年輕代區大小和相應的 Survivor 區比例,以達到目標系統規定的最低相應時間或者收集頻率等,此值建議使用并行收集器時,一直打開,
  • -XX:CMSFullGCsBeforeCompaction=n:由于并發收集器不對記憶體空間進行壓縮、整理,所以運行一段時間以后會產生“碎片”,使得運行效率降低,此值設定運行多少次 GC 以后對記憶體空間進行壓縮、整理,
  • -XX:+UseCMSCompactAtFullCollection:打開對年老代的壓縮,可能會影響性能,但是可以消除碎片

收集器設定經驗分享

關于收集器的選擇 JVM 給了三種選擇:串行收集器、并行收集器、并發收集器,但是串行收集器只適用于小資料量的情況,所以這里的選擇主要針對并行收集器和并發收集器,

默認情況下,JDK5.0 以前都是使用串行收集器,如果想使用其他收集器需要在啟動時加入相應引數,JDK5.0 以后,JVM 會根據當前系統配置進行判斷,

常見配置:
并行收集器主要以到達一定的吞吐量為目標,適用于科學計算和后臺處理等,
-Xmx3800m -Xms3800m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20
使用 ParallelGC 作為并行收集器, GC 執行緒為 20(CPU 核心數>=20 時),記憶體問題根據
硬體配置具體提供,建議使用物理記憶體的 80%左右作為 JVM 記憶體容量,

-Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC
指定老年代收集器,在JDK5.0之后的版本,ParallelGC對應的全收集器就是ParallelOldGC,可以忽略

-Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100
指定 GC 時最大暫停時間,單位是毫秒,每次 GC 最長使用 100 毫秒,可以盡可能提高作業執行緒的執行資源,

-Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100 -XX:+UseAdaptiveSizePolicy
UseAdaptiveSizePolicy 是提高年輕代 GC 效率的配置,次收集器執行效率,

并發收集器主要是保證系統的回應時間,減少垃圾收集時的停頓時間,適用于應用服務
器、電信領域、互聯網領域等,
-Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC

指定年輕代收集器為 ParNew,年老代收集器 ConcurrentMarkSweep,并發 GC 執行緒數為20(CPU 核心>=20),并發 GC 的執行緒數建議使用(CPU 核心數+3)/4 或 CPU 核心數【不推薦使用】,

-Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC
-XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection
CMSFullGCsBeforeCompaction=5 執行 5 次 GC 后,運行一次記憶體的整理,
UseCMSCompactAtFullCollection 執行老年代記憶體整理,可以避免記憶體碎片,提高 GC 過
程中的效率,減少停頓時間,

總結

  • 新生代大小選擇
    (1)回應時間優先的應用:盡可能設大,直到接近系統的最低回應時間限制(根據實際情況選擇) , 在此種情況下, 新生代收集發生的頻率也是最小的 , 同時 , 減少到達老年代的物件,
    (2)吞吐量優先的應用:盡可能的設定大,可能到達 Gbit 的程度,因為對回應時間沒有要求,垃圾收集可以并行進行,一般適合 8CPU 以上的應用,

  • 年老代大小選擇
    回應時間優先的應用:年老代使用并發收集器,所以其大小需要小心設定,一般要考慮并發會話率和會話持續時間等一些引數,如果堆設定小了,可以會造成記憶體碎片、高回收頻率以及應用暫停而使用傳統的標記清除方式;如果堆大了,則需要較長的收集時間,
    最優化的方案,一般需要參考以下資料獲得:
    (1)并發垃圾收集資訊
    (2)持久代并發收集次數
    (3)傳統 GC 資訊
    (4)花在年輕代和年老代回收上的時間比例
    (5)減少年輕代和年老代花費的時間,一般會提高應用的效率

吞吐量優先的應用:一般吞吐量優先的應用都有一個很大的年輕代和一個較小的年老代,原因是,這樣可以盡可能回收掉大部分短期物件,減少中期的物件,而年老代存放長期存活物件,

較小堆引起的碎片問題,因為年老代的并發收集器使用標記、清除演算法,所以不會對堆進行壓縮,當收集器回收時,他會把相鄰的空間進行合并,這樣可以分配給較大的物件,但是,當堆空間較小時,運行一段時間以后,就會出現“碎片”,如果并發收集器找不到足夠的空間,那么并發收集器將會停止,然后使用傳統的標記、整理方式進行回收,如果出現“碎片”,可能需要進行如下配置:

-XX:+UseCMSCompactAtFullCollection:使用并發收集器時,開啟對年老代的壓縮,
-XX:CMSFullGCsBeforeCompaction=0:上面配置開啟的情況下,這里設定多少次 Full GC后,對年老代進行壓縮

【關于JVM調優這一塊,后面再花時間詳細整理描述,此處不再說明】

JVM面試題

待更新…

參考文章

類加載參考:https://zhuanlan.zhihu.com/p/44670213
雙親委派機制參考:https://www.jianshu.com/p/1e4011617650
沙箱安全機制參考:https://www.cnblogs.com/MyStringIsNotNull/p/8268351.html
堆疊參考:https://baike.baidu.com/item/%E6%A0%88/12808149?fr=aladdin
新生代參考:http://ifeve.com/jvm-yong-generation/
永久代參考:https://www.cnblogs.com/paddix/p/5309550.html
GC參考文章:https://www.jianshu.com/p/ee3e9dff5700
GC參考文章:CSDN

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/171370.html

標籤:其他

上一篇:第十六章 陣列

下一篇:隱寫工具stegpy的安裝

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more