JVM的運行數據區相

来源:https://www.cnblogs.com/jiagooushi/archive/2022/09/23/16722690.html
-Advertisement-
Play Games

大家好,,這篇文章咱們聊下JVM性能優化的問題 這篇文章主要介紹下JVM的運行數據區相關的內容,包括: 程式計數器 虛擬機棧 本地方法棧 堆 方法區 案例 和總結 好了,開始乾貨環節~ 作為一個常識性的知識,大家都知道位元組碼只是一個二進位文件存放在那裡。要想在jvm里跑起來,先得有個運行的記憶體環境。 ...


大家好,,這篇文章咱們聊下JVM性能優化的問題

這篇文章主要介紹下JVM的運行數據區相關的內容,包括:

  • 程式計數器
  • 虛擬機棧
  • 本地方法棧
  • 方法區
  • 案例 和總結

好了,開始乾貨環節~

作為一個常識性的知識,大家都知道位元組碼只是一個二進位文件存放在那裡。要想在jvm里跑起來,先得有個運行的記憶體環境。

也就是我們所說的jvm運行時數據區。

1)運行時數據區的位置

運行時數據區是jvm中最為重要的部分,執行引擎頻繁操作的就是它。類的初始化,以及後面我們講的對象空間的分配、垃圾的回收都是在這塊區域發生的。
)

image.png
2)區域劃分

根據《Java虛擬機規範》中的規定,在運行時數據區將記憶體細分為幾個部分

線程私有的:Java虛擬機棧(Java Virtual Machine Stack)、程式計數器(Program Counter Register)、本地方法棧(Native Method Stacks)

大家共用的:方法區(Method Area)、Java堆區(Java Heap)

image.png

接下來我們分塊詳細來解讀,每一塊是做什麼的,如果溢出了會發生什麼事情

3.1 程式計數器

3.1.1 概述

程式計數器(Program Counter Register)

  • 每個線程一個。是一塊較小的記憶體空間,它表示當前線程執行的位元組碼指令的地址。
  • 位元組碼解釋器工作時,通過改變這個計數器的值來選取下一條需要執行的位元組碼指令,所以整個程式無論是分支、迴圈、跳轉、異常處理、線程恢復等基礎功能都需要依賴這個計數器來完成。
  • 由於線程是多條並行執行的,互相之間執行到哪條指令是不一樣的,所以每條線程都需要有一個獨立的程式計數器,各條線程之間計數器互不影響,獨立存儲,我們稱這類記憶體區域為“線程私有”的記憶體。
  • 如果是native方法,這裡為空

3.1.2 溢出異常

沒有!

在虛擬機規範中,沒有對這塊區域設定記憶體溢出規範,也是唯一一個不會溢出的區域

3.1.3 案例

因為它不會溢出,所以我們沒有辦法給它造一個,但是從class類上可以找到痕跡。

回顧上面javap的反彙編,其中code所對應的編號就可以理解為計數器中所記錄的執行編號。

image.png

3.2 虛擬機棧

image.png

3.2.1 概述

  • 也是線程私有的!生命周期與線程相同。
  • 它描述的是Java方法執行的當前線程的記憶體模型,每個方法被執行的時候,Java虛擬機都會同步創建一個棧幀,用於存儲局部變數表、操作數棧、動態連接、方法出口等信息。每一個方法被調用直至執行完畢的過程,就對應著一個棧幀在虛擬機棧中從入棧到出棧的過程。

3.2.2 溢出異常

1)棧深度超出設定

如果是創建的棧的深度大於虛擬機允許的深度,拋出

Exception in thread "main" java.lang.StackOverflowError

2)記憶體申請不足

如果棧允許記憶體擴展,但是記憶體申請不夠的時候,拋出 OutOfMemoryError

註意!這一點和具體的虛擬機有關,hotspot虛擬機並不支持棧空間擴展,所以單線程環境下,一個線程創建時,分配給它固定大小的一個棧,在這個固定棧空間上不會出現再去擴容申請記憶體的情況,也就不會遇到申請不到一說,只會因為深度問題超出固定空間造成上面的StackOverflowError

如果換成多線程,毫無節制的創建線程,還是有可能造成OutOfMemoryError。但是這個和Xss棧空間大小無關。是因為線程個數太多,棧的個數太多,導致系統分配給jvm進程的物理記憶體被吃光。

這時候虛擬機會附帶相關的提示:

Exception in thread "main" java.lang.OutOfMemoryError: unable to create native thread

ps: 每個線程預設分配1M空間(64位linux,hotspot環境)

疑問:是不是改小Xss的值就可以得到棧空間溢出呢?

答:根據上面的分析,hotspot下不可以,還是會拋出StackOverflowError,無非深度更小了。

3.2.3 案例一:進出棧順序

1)代碼

 package com.itheima.jvm.demo;
 ​
 /**
  * 程式模擬進棧、出棧過程
  * 先進後出
  */
 public class StackInAndOut {
     /**
      * 定義方法一
      */
     public static void A() {
         System.out.println("進入方法A");
     }
 ​
     /**
      * 定義方法二;調用方法一
      */
     public static void B() {
         A();
         System.out.println("進入方法B");
     }
 ​
     public static void main(String[] args) {
 ​
         B();
         System.out.println("進入Main方法");
     }
 }
 ​
 ​
 ​

2)運行結果:

 進入方法A
 進入方法B
 進入Main方法

3)棧結構:

main方法---->B方法---->A方法

image.png

3.2.4 案例二:棧深度溢出

1)代碼

這個容易實現,方法嵌套自己就可以:

package com.itheima.jvm.demo;

/**
 * 通過一個程式模擬線程請求的棧深度大於虛擬機所允許的棧深度;
 * 拋出StackOverflowError
 */
public class StackOverFlow {
    /**
     * 定義方法,迴圈嵌套自己
     */
    public static void B() {
        B();
        System.out.println("進入方法B");
    }

    public static void main(String[] args) {

        B();
        System.out.println("進入Main方法");
    }
}

2)運行結果:

Exception in thread "main" java.lang.StackOverflowError
	at com.itheima.jvm.demo.StackOverFlow.B(StackOverFlow.java:12)
	at com.itheima.jvm.demo.StackOverFlow.B(StackOverFlow.java:12)
	at com.itheima.jvm.demo.StackOverFlow.B(StackOverFlow.java:12)
	at com.itheima.jvm.demo.StackOverFlow.B(StackOverFlow.java:12)
	at com.itheima.jvm.demo.StackOverFlow.B(StackOverFlow.java:12)

3)棧結構:

image.png

image.png

3.2.5 案例三:棧記憶體溢出

一直不停的創建線程就可以堆滿棧

但是!這個很危險,到32系統的winxp上勇敢的小伙伴可以試一試,機器卡死不負責!

package com.itheima.jvm.demo;

/*
* 棧記憶體溢出,註意!很危險,謹慎執行
* 執行時可能會卡死系統。直到記憶體耗盡
* */
public class StackOutOfMem {
    public static void main(String[] args) {
        while (true) {
            new Thread(() -> {
                while(true);
            }).start();
        }
    }
}

3.3 本地方法棧

3.3.1 概述

  • 本地方法棧的功能和特點類似於虛擬機棧,均具有線程隔離的特點
  • 不同的是,本地方法棧服務的對象是JVM執行的native方法,而虛擬機棧服務的是JVM執行的java方法
  • 虛擬機規範里對這塊所用的語言、數據結構、沒有強制規定,虛擬機可以自由實現它
  • 甚至,hotspot把它和虛擬機棧合併成了1個

3.3.2 溢出異常

和虛擬機棧一樣,也是兩個:

如果是創建的棧的深度大於虛擬機允許的深度,拋出 StackOverFlowError

記憶體申請不夠的時候,拋出 OutOfMemoryError

3.4 堆

3.4.1 概述

與上面的3個不同,堆是所有線程共用的!所謂的線程安全不安全也是出自這裡。

在虛擬機啟動時創建。此記憶體區域的唯一目的就是存放對象實例,Java世界里“幾乎”所有的對象實例都在這裡分配記憶體。

需要註意的是,《Java虛擬機規範》並沒有對堆進行細緻的劃分,所以對於堆的講解要基於具體的虛擬機,我們以使用最多的HotSpot虛擬機為例。

Java堆是垃圾收集器管理的記憶體區域,因此它也被稱作“GC堆”,這就是我們做JVM調優的重點區域部分。

3.4.2 jdk1.7

jvm的記憶體模型在1.7和1.8有較大的區別,雖然1.7目前使用的較少了,但是我們也是需要對1.7的記憶體模型有所瞭解,所以接下里,我們將先學習1.7再學習1.8的記憶體模型。

image.png

  • Young 年輕區(代)

    Young區被劃分為三部分,Eden區和兩個大小嚴格相同的Survivor區

    其中,Survivor區間中,某一時刻只有其中一個是被使用的,另外一個留做垃圾收集時複製對象用

    在Eden區間變滿的時候, GC就會將存活的對象移到空閑的Survivor區間中,根據JVM的策略,在經過幾次垃圾收集後,任然存活於Survivor的對象將被移動到下麵的Tenured區間。

  • Tenured 年老區

    Tenured區主要保存生命周期長的對象,一般是一些老的對象,當一些對象在Young複製轉移一定的次數以後,對象就會被轉移到Tenured區,一般如果系統中用了application級別的緩存,緩存中的對象往往會被轉移到這一區間。

  • Perm 永久區

    hotspot 1.6 才有這貨,現在已經成為歷史

    Perm代主要保存class,method,filed對象,這部份的空間一般不會溢出,除非一次性載入了很多的類,不過在涉及到熱部署的應用伺服器的時候,有時候會遇到java.lang.OutOfMemoryError : PermGen space 的錯誤,造成這個錯誤的很大原因就有可能是每次都重新部署,但是重新部署後,類的class沒有被卸載掉,這樣就造成了大量的class對象保存在了perm中,這種情況下,一般重新啟動應用伺服器可以解決問題。另外一種可能是創建了大批量的jsp文件,造成類信息超出perm的上限而溢出。這種重啟也解決不了。只能調大空間。

  • Virtual區:

    jvm參數可以設置一個範圍,最大記憶體和初始記憶體的差值,就是Virtual區。

3.4.3 jdk1.8

image.png

由上圖可以看出,jdk1.8的記憶體模型是由2部分組成,年輕代 + 年老代。永久代被幹掉,換成了Metaspace(元數據空間)

年輕代:Eden + 2*Survivor (不變)

年老代:OldGen (不變)

元空間:原來的perm區 (重點!)

需要特別說明的是:Metaspace所占用的記憶體空間不是在虛擬機內部,而是在本地記憶體空間中,這也是與1.7的永久代最大的區別所在。

image.png

3.4.4 溢出異常

記憶體不足時,拋出

java.lang.OutOfMemoryError: Java heap space

3.4.5 案例:堆溢出

1)代碼

分配大量對象,超出jvm規定的堆範圍即可

package com.itheima.jvm.demo;

import java.util.ArrayList;
import java.util.List;

/**
 * 堆溢出
 *   -Xms20m -Xmx20m
 */
public class HeapOOM {
    Byte[] bytes = new Byte[1024*1024];
    public static void main(String[] args) {
        List list = new ArrayList();
        int i = 0;
        while (true) {
            System.out.println(++i);
            list.add(new HeapOOM());
        }
    }
}

2)啟動

註意啟動時,指定一下堆的大小:

image.png

2)輸出

1
2
3
4
5
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at com.itheima.jvm.demo.HeapOOM.<init>(HeapOOM.java:7)
	at com.itheima.jvm.demo.HeapOOM.main(HeapOOM.java:13)

3.5 方法區

3.5.1 概述

同樣,線程共用的。

它主要用來存儲類的信息、類里定義的常量、靜態變數、編譯器編譯後的代碼緩存。

註意!方法區在虛擬機規範里這是一個邏輯概念,它具體放在那個區域里沒有嚴格的規定。

所以,hotspot 1.7 將它放在了堆的永久代里,1.8+單獨開闢了一塊叫metaspace來存放一部分內容(不是全部!定義的類對象在堆里)

具體方法區主要存什麼東西呢?粗略的分,可以劃分為兩類:

  • 類信息:主要指類相關的版本、欄位、方法、介面描述、引用等

  • 運行時常量池:編譯階段生成的常量與符號引用、運行時加入的動態變數

    (常量池裡的類變數,如對象或字元串,比較特殊,1.6和1.8位置不同,下麵會講到)

小提示:

這裡經常會跟上面堆里的永久代混為一談,實際上這是兩碼事

永久代是hotspot在1.7及之前才有的設計,1.8+,以及其他虛擬機並不存在這個東西。

可以說,永久代是1.7的hotspot偷懶的結果,他在堆里劃分了一塊來實現方法區的功能,叫永久代。因為這樣可以藉助堆的垃圾回收來管理方法區的記憶體,而不用單獨為方法區再去編寫記憶體管理程式。懶惰!

同時代的其他虛擬機,如J9,Jrockit等,沒有這個概念。後來hotspot認識到,永久代來做這件事不是一個好主意。1.7已經從永久代拿走了一部分數據,直到1.8+徹底去掉了永久代,方法區大部分被移到了metaspace(再強調一下,不是全部!)

結論:

方法區是一定存在的,這是虛擬機規定的,但是是個邏輯概念,在哪裡虛擬機自己去決定

而永久代不一定存在(hotspot 1.7 才有),已成為歷史

3.5.2 溢出異常

1.6:OutOfMemoryError: PermGen space

1.8:OutOfMemoryError: Metaspace

3.5.3 案例:1.6方法區溢出

1)原理

image.png

在1.6里,字元串常量是運行時常量池的一部分,也就是歸屬於方法區,放在了永久代里。

所以1.6環境下,讓方法區溢出,只需要可勁造往字元串常量池中造字元串即可,這裡用到一個方法:

/*
如果字元串常量池裡有這個字元串,直接返回引用,不再額外添加
如果沒有,加進去,返回新創建的引用
*/
String.intern()

2)代碼

/**
 * 方法區溢出,註意限制一下永久代的大小
 * 編譯的時候註意pom里的版本,要設置1.6,否則啟動會有問題
 * jdk1.6  :     -XX:PermSize=6M -XX:MaxPermSize=6M
 */
public class ConstantOOM {

    public static void main(String[] args) {
        ConstantOOM oom = new ConstantOOM();
        Set<String> stringSet = new HashSet();
        int i = 0;
        while (true) {
            System.out.println(++i);
            stringSet.add(String.valueOf(i).intern());
        }
    }
}

3)創建啟動環境

image.png

4)異常信息:

...
19118
19119
19120
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
	at java.lang.String.intern(Native Method)
	at com.itheima.jvm.demo.ConstantOOM.main(ConstantOOM.java:19)

2.5.4 案例:1.8方法區溢出

1)到了1.8,情況發生了變化

可以測試一下,1.8下無論指定下麵的哪個參數,常量池運行都不會溢出,會一直列印下去

-XX:PermSize=6M -XX:MaxPermSize=6M
-XX:MetaspaceSize=10M -XX:MaxMetaspaceSize=10M

2)配置運行環境

image.png

3)控制台信息

不會拋出異常,只要你jvm堆記憶體夠,理論上可以一直打下去

image.png

4)為什麼呢?

永久代我們加了限制,結果沒意義,因為1.8里已經沒有這貨了

元空間也加了限制,同樣沒意義,那說明字元串常量池它不在元空間里!

那麼,它在哪裡呢?

image.png

jdk1.8以後,字元串常量池被移到了堆空間,和其他對象一樣,接受堆的控制。

其他的運行時的類信息、基本數據類型等在元空間。

我們可以驗證一下,對上面的運行時參數再加一個堆上限限制:

-Xms10m
-Xmx10m

運行環境如下:

image.png

運行沒多久,你會得到以下異常:

……
84014
84015
84016
84017
84018
84019
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
	at java.lang.Integer.toString(Integer.java:403)
	at java.lang.String.valueOf(String.java:3099)
	at com.itheima.jvm.demo.ConstantOOM.main(ConstantOOM.java:18)

說明:1.8里,字元串inter()被放在了堆里,受最大堆空間的限制。

5)那如何才能讓元空間溢出呢?

既然字元串常量池不在這裡,那就換其他的。類的基本信息總在元空間吧?我們來試一下

cglib是一個apache下的位元組碼庫,它可以在運行時生成大量的對象,我們while迴圈同時限制metaspace試試:

附:https://gitee.com/mirrors/cglib (想深入瞭解這個工具的猛擊左邊,這裡不做過多討論)

package com.itheima.jvm.demo;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * jdk8方法區溢出
 *   -XX:MetaspaceSize=10M -XX:MaxMetaspaceSize=10M
 */
public class ConstantOOM8 {
    public static void main(final String[] args) {
        while (true) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(OOM.class);
            enhancer.setUseCache(false);
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                    return methodProxy.invokeSuper(objects,args);
                }
            });
            enhancer.create();
        }
    }

    static class OOM{

    }
}

6)運行設置

image.png

7)運行結果

Caused by: java.lang.OutOfMemoryError: Metaspace
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763)

結論:

jdk8引入元空間來存儲方法區後,記憶體溢出的風險比歷史版本小多了,但是在類超出控制的時候,依然會打爆方法區

3.6 一個案例

為便於大家理解和記憶,下麵我們用一個案例,把上面各個區串通起來。

假設有個Bootstrap的類,執行main方法。在jvm里,它從class文件到跑起來,大致經過如下步驟:

image.png

  1. 首先JVM會先將這個Bootstrap.class 信息載入到記憶體中的方法區
  2. 接著,主線程開闢一塊記憶體空間,準備好程式計數器pc,虛擬機棧、本地方法棧
  3. 然後,JVM會在Heap堆上為Bootstrap.class 創建一個Bootstrap.class 的類實例
  4. JVM開始執行main方法,這時在虛擬機棧里為main方法創建一個棧幀
  5. main方法在執行的過程之中,調用了greeting方法,則JVM會為greeting方法再創建一個棧幀,推到虛擬機棧頂,在main的上面,每次只有一個棧幀處於活動狀態,當前為greeting
  6. 當greeting方法運行完成後,則greeting方法出棧,當前活動幀指向main,方法繼續往下運行

3.7 歸納總結

image.png

1)獨享/共用的角度:

  • 獨享:程式計數器、虛擬機棧、本地方法棧
  • 共用:堆、方法區

2)error的角度:

  • 程式計數器:不會溢出,比較特殊,其他都會
  • 兩個棧:可能會發生兩種溢出,一是深度超了,報StackOverflowError,空間不足:OutOfMemoryError
  • 堆:只會在空間不足時,報OutOfMemoryError,會提示heapSpace
  • 方法區:空間不足時,報OutOfMemoryError,提示不同,1.6是permspace,1.8是元空間,和它在什麼地方有關

3)歸屬:

  • 計數器、虛擬機棧、本地方法棧:線程創建必須申請配套,真正的物理空間
  • 堆:真正的物理空間,但是內部結構的劃分有變動,1.6有永久代,1.8被幹掉
  • 方法區:最沒歸屬感的一塊,原因就是它是一個邏輯概念。1.6被放在了堆的永久代,1.8被拆分,一部分在元空間,一部分(方法區的運行時常量池裡面的類對象,包括字元串常量,被設計放在了堆里)
  • 直接記憶體:這塊實際上不屬於運行時數據區的一部分,而是直接操作物理記憶體。在nio操作里DirectByteBuffer類可以對native操作,避免流在堆內外的拷貝。我們下一步的調優不會涉及到它,瞭解即可。

好了,關於JVM運行數據區的內容,就告一段落了,如果大家覺得有幫助的話,歡迎點贊 拍磚

您的認可是我們高產的動力

下期預告:
類載入
對象創建
對象銷毀

往期乾貨:

本文由傳智教育博學谷教研團隊發佈。

如果本文對您有幫助,歡迎關註點贊;如果您有任何建議也可留言評論私信,您的支持是我堅持創作的動力。

轉載請註明出處!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 多用戶即時通訊系統04 4.編碼實現03 4.7功能實現-伺服器推送消息功能實現 4.7.1思路分析 伺服器推送新聞,本質其實就是群發消息 在伺服器啟動一個獨立線程,專門負責推送新聞 該線程通過管理線程的集合,把所有的線程的socket拿到,併發送Messgae對象信息 客戶端通過接收,自然就拿到了 ...
  • 多用戶即時通訊系統04 4.編碼實現03 4.6功能實現-發送文件功能實現 4.6.1思路分析 客戶端(發送者): 先把文件a.jpg讀取到客戶端的位元組數組 把文件對應的位元組數組封裝到message對象[包含文件內容,發送者,接收者] 將message對象發送到服務端 服務端: 接收到message ...
  • 在子類派生的新方法中如何重用父類的功能 方式一:指名道姓調用某一個類下的函數=》不依賴於繼承關係 class OldboyPeople: def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex def f1( ...
  • 多用戶即時通訊系統04 4.編碼實現03 4.5功能實現-群聊功能實現 4.5.1思路分析 群聊的實現思路和私聊的實現非常類似。 不同的是:私聊時,服務端接收到消息後,只需要找出接收方的socket併發送消息即可 群聊時,服務端在接收到消息後需要遍歷集合中所有的線程,找出除了發送方的所有客戶端的so ...
  • 一、聚合查詢 需要導入模塊:from django.db.models import Max, Min, Sum, Count, Avg 關鍵語法:aggregate(聚合結果別名 = 聚合函數(參數)) 查詢結果:使用聚合函數,從每一個組中獲取結果:字典 註意點: 1 聚合函數必須在分組之後才能使 ...
  • java基礎 以下內容為本人的學習筆記,如需要轉載,請聲明原文鏈接 https://www.cnblogs.com/lyh1024/p/16720908.html Ø 冒泡排序原理: 比較數組中,兩個相鄰的元素,如果第一個數比第二個數大,我們就交換他們的位置; 每一次比較,都會產生出一個最大,或者最 ...
  • SSD實現思路 SSD具有如下主要特點: 從YOLO中繼承了將detection轉化為regression的思路,一次完成目標定位與分類 基於Faster RCNN中的Anchor,提出了相似的Prior box; 加入基於特征金字塔(Pyramidal Feature Hierarchy)的檢測方 ...
  • 本文按照mac講解protobuf的安裝,windows上比較好安裝按照mac的基本流程就可以安裝成功,mac上的安裝有的時候比較容易出現問題 一、通過brew的方式安裝(僅Mac) 需要mac中存在brew,輸入命令:brew --version 查看是否存在brew,如不存在就進行安裝,安裝方法 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...