JVM筆記【一】java和Tomcat類加載機制

JVM筆記一java和Tomcat類加載機制

java和Tomcat類加載機制
  • Java類加載

  • * loadClass加載步驟
    
    • 類加載機制
    • 類加載器初始化過程
    • 雙親委派機制
    • 全盤負責委托機制
    • 類關系圖
    • 自定義類加載器
    • 打破雙親委派機制
  • Tomcat類加載器

  • * 為了解決以上問題,tomcat是如何實現類加載機制的?
    

Java類加載

當我們用java命令運行某個類的main函數啟動程序時,首先需要通過類加載器把主類加載到jvm中。類似流程圖如下。
加載過程圖

loadClass加載步驟

類全生命周期:加載 >>驗證>>準備>>解析>>初始化>>使用>>卸載。
加載周期

加載:在硬盤上查找并通過IO兌入字節碼文件,使用到類時才會加載,例如調用類的main方法,new對象等等,在加載階段會在內存中生成一個代表這個類的java.lang.Class對象(推薦),放在堆中,作為訪問這個類在方法區中類元數據的各個數據的接口。
驗證:校驗字節碼文件的正確性。如:文件格式的驗證,元數據的驗證,字節碼的驗證,符號引用的驗證。
準備:給類的靜態變量分配內存,并賦予默認值,此處給默認值,不一定是我們程序中賦予的值。如果被final修飾,在編譯的時候會給屬性添加ConstantValue屬性,準備階段直接完成賦值,即沒有賦初值這一步

解析:將符號引用 替換為直接引用,該階段會把一些靜態方法(符號引用,比如main()方法)替換為指向數據所存內存的指針或者句柄等(直接引用),這是所謂的靜態鏈接 過程(類加載期間完成),動態鏈接 是在程序運行期間完成的,將符號引用替換為直接引用(// TODO 待補充),解析后的信息存儲在ConstantPoolCache類實例中
初始化:對類的靜態變量初始化為指定的值,執行靜態代碼塊,比如:initData 的666值是此時才賦予的。構造放在在靜態代碼塊之后。

public static final int initData=666;

v2

類被加載到方法區中后,主要包含運行時常量池、類型信息、字段信息、方法信息、類加載器的引用、對應class實例的引用等信息。
類加載器的引用 :這個類到 類加載器實例 的引用。
對應class實例的引用 :類加載器在加載類信息放到方法區中后,會創建一個對應的Class類型的對象實例放到堆(Heap)中,作為開發人員訪問方法區中類定義的入口和切入點。
注意:主類在運行過程中如果使用到其他類,會逐步加載這些列。jar包或者war包里的類不是一次性全部加載的,是使用到時才加載。代碼示例如下:

public class TestDynamicLoad {static {System.out.println("*************load TestDynamicLoad************");}public static void main(String[] args) {new A();System.out.println("*************load test************");B b = null; //B不會加載,除非這里執行 new B()}}class A {static {System.out.println("*************load A************");}public A() {System.out.println("*************initial A************");}
}class B {static {System.out.println("*************load B************");}public B() {System.out.println("*************initial B************");}
}

結果:

*************load TestDynamicLoad************
*************load A************
*************initial A************
*************load test************

類加載機制

類加載過程主要是通過類加載器來實現的,java里有如下幾種類加載器。

  • 引導類加載器(bootstrapLoader):負責加載支撐JVM運行的位于JRE的lib目錄下的核心類庫,比如:rt.jar、charsets.jar等
  • 擴展類加載器(ExtClassLoader):負責加載支撐JVM運行的位于JRE的lib目錄下的ext擴展目錄中的JAR類包
  • 應用程序類加載器(AppClassLoader):負責加載ClassPath路徑下的類包,主要就是加載自己寫的那些類。自定義加載器:負責加載用戶自定義路徑下的類包。

類加載器初始化過程

由上面的加載過程圖可知,JVM啟動,實例sun.misc.Launcher類,而sun.misc.Launcher構造方法內部,創建了兩個類加載器,分別是sun.misc.Launcher.ExtClassLoader(擴展類加載器)和sun.misc.Launcher.AppClassLoader(應用類加載器)。

JVM會默認調用Launcher中的getClassLoader()方法,返回的加載器會是AppClassLoader來加載我們的應用程序。代碼截取

//Launcher的構造方法
public Launcher() {Launcher.ExtClassLoader var1;try {//構造擴展類加載器,在構造的過程中將其父加載器設置為nullvar1 = Launcher.ExtClassLoader.getExtClassLoader();} catch (IOException var10) {throw new InternalError("Could not create extension class loader", var10);}try {//構造應用類加載器,在構造的過程中將其父加載器設置為ExtClassLoader,//Launcher的loader屬性值是AppClassLoader,我們一般都是用這個類加載器來加載我們自
己寫的應用程序this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);} catch (IOException var9) {throw new InternalError("Could not create application class loader", var9);}Thread.currentThread().setContextClassLoader(this.loader);String var2 = System.getProperty("java.security.manager");。。。 。。。 //省略一些不需關注代碼}public ExtClassLoader(File[] var1) throws IOException {// Launcher.ExtClassLoader.getExtClassLoader(); 實例化會走到地方,他父加載器傳的是nullsuper(getExtURLs(var1), (ClassLoader)null, Launcher.factory);SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
}

雙親委派機制

JVM類加載器是有親子層級結構的,如下圖:
雙親委派圖

這里類加載其實就是一個雙親委派機制,加載某個類時,launcher的getClassLoader會給出appClassLoader這個加載器,調用其最上層抽象ClassLoader的loadClass方法,其流程是會先找自己有沒**有加載過(并不是加載)**這個類,如果有直接返回,如果沒有,則委托父加載器尋找目標類,而此時父加載器(擴展類加載器)同樣是實現ClassLoader的類,同樣走loadClass方法,邏輯也是找自身是否加載過此類,如果沒有,則繼續委托其父加載器;如果依然找不到目標類,則在自己的類加載路徑中查找并載入目標類。

比如我們的Math類,最先會找應用程序類加載器加載,應用程序類加載器會先委托擴展類加載 器加載,擴展類加載器再委托引導類加載器,頂層引導類加載器在自己的類加載路徑里找了半天沒找到Math類,則向下退回加載Math類的請求,擴展類加載器收到回復就自己加載,在自己的類加載路徑里找了半天也沒找到Math類,又向下退回Math類的加載請求給應用程序類加載器,應用程序類加載器于是在自己的類加載路徑里找Math類,結果找到了就自己加載了。

//ClassLoader的loadClass方法,里面實現了雙親委派機制
protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {//  // 檢查當前類加載器是否已經加載了該類Class<?> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {if (parent != null) { //如果當前加載器父加載器不為空則委托父加載器加載該類c = parent.loadClass(name, false);} else { //如果當前加載器父加載器為空則委托引導類加載器加載該類c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}if (c == null) {// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();//都會調用URLClassLoader的findClass方法在加載器的類路徑里查找并加載該類c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) { //不會執行resolveClass(c);}return c;}

雙親委派說簡單的,就是先找父親加載,不行再有兒子自己加載,此處注意:父類加載器和父類不是一個概念。

全盤負責委托機制

“全盤負責”是指當一個ClassLoader裝載一個類時,除非顯示的使用另一個ClassLoader,該類所有依賴及引用的類也有這個ClassLoader一次載入完畢。(因為正常情況下,依賴和引用類是只有在使用的時候才加載的。)

類關系圖

classLoader類繼承圖

自定義類加載器

自定義類加載器只需要繼承java.lang.ClassLoader類,該類由兩個核心方法,一個是loadClass(String,boolean),實現了雙親委派機制,還有一個是findClass,默認實現是空方法,所以我們自定義類加載器主要是重寫findClass方法(app和ext因為都是繼承URLClassLoader,此方法在URLClassLoader中實現了)。

package com.tuling.jvm;import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Method;/*** @Description: 自定義類加載器* @ClassName: MyClassLoaderTest* @Author: * @Date: 2021/8/11 0:42**/
public class MyClassLoaderTest {static class MyClassLoader extends ClassLoader {private String classPath;public MyClassLoader(String classPath) {this.classPath = classPath;}private byte[] loadByte(String name) throws Exception {name = name.replaceAll("\\.", "/");FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class");int len = fis.available();byte[] data = new byte[len];fis.read(data);fis.close();return data;}protected Class<?> findClass(String name) throws ClassNotFoundException {try {byte[] data = loadByte(name);//defineClass將一個字節數組轉為Class對象,這個字節數組是class文件讀取后最終的字節 數組。return defineClass(name, data, 0, data.length);} catch (Exception e) {e.printStackTrace();throw new ClassNotFoundException();}}}public static void main(String args[]) throws Exception {//初始化自定義類加載器,會先初始化父類ClassLoader,其中會把自定義類加載器的父加載 器設置為應用程序類加載器AppClassLoaderMyClassLoader classLoader = new MyClassLoader("D:"+ File.separator+"program_test");//D盤創建 test/com/tuling/jvm 幾級目錄,將User類的復制類User1.class丟入該目錄Class clazz = classLoader.loadClass("com.tuling.jvm.User1");Object obj = clazz.newInstance();Method method = clazz.getDeclaredMethod("sout", null);method.invoke(obj, null);System.out.println(clazz.getClassLoader().getClass().getName());}
}

打破雙親委派機制

package com.tuling.jvm;import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Method;/*** @Description: 雙親委派和反* @ClassName: MyClassLoaderTest* @Author: * @Date: 2021/8/11 0:42**/
public class MyClassLoaderTest {static class MyClassLoader extends ClassLoader {private String classPath;public MyClassLoader(String classPath) {this.classPath = classPath;}private byte[] loadByte(String name) throws Exception {name = name.replaceAll("\\.", "/");FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class");int len = fis.available();byte[] data = new byte[len];fis.read(data);fis.close();return data;}protected Class<?> findClass(String name) throws ClassNotFoundException {try {byte[] data = loadByte(name);//defineClass將一個字節數組轉為Class對象,這個字節數組是class文件讀取后最終的字節 數組。return defineClass(name, data, 0, data.length);} catch (Exception e) {e.printStackTrace();throw new ClassNotFoundException();}}/*** 重寫類加載方法,實現自己的加載邏輯,不委派給雙親加載** @param name* @param resolve* @return* @throws ClassNotFoundException*/protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException {synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass<?> c = findLoadedClass(name);if (c == null) {// 特定類打破 這里的判斷和實現方式可以更換,思想是這樣if (name.startsWith("com.tuling.jvm")) {c = findClass(name);} else {c = super.loadClass(name, false);}}if (resolve) {resolveClass(c);}return c;}}}public static void main(String args[]) throws Exception {//初始化自定義類加載器,會先初始化父類ClassLoader,其中會把自定義類加載器的父加載 器設置為應用程序類加載器AppClassLoaderMyClassLoader classLoader = new MyClassLoader("D:" + File.separator + "program_test");//D盤創建 test/com/tuling/jvm 幾級目錄,將User類的復制類User1.class丟入該目錄Class clazz = classLoader.loadClass("com.tuling.jvm.User1");Object obj = clazz.newInstance();Method method = clazz.getDeclaredMethod("sout", null);method.invoke(obj, null);System.out.println(clazz.getClassLoader().getClass().getName());}
}

Tomcat類加載器

思考:tomcat是web容器,他需要解決什么問題?

  1. 一個web容器可能需要部署兩個及以上的應用程序,不同的應用程序可能會依賴同一個第三方類庫的不同版本 ,不能要求同一個類庫在同一個服務器只有一份,因此要保證每個應用程序的類庫都是獨立的,保證相互隔離。
  2. 部署在同一個web容器中的**相同的類庫相同的版本可以共享。**否則,如果服務器有10個應用程序,那么要有10分相同的類庫加載進入虛擬機
  3. **web容器也有自己依賴的類庫,不能與應用程序的類庫混淆,**基于安全考慮,應該讓容器的類庫和程序的類庫隔離開來。
  4. web容器要支持JSP的修改,jsp文件最終也要編譯成class文件才能在虛擬機中運行,但是jsp修改頻繁,需要熱加載。

為了解決以上問題,tomcat是如何實現類加載機制的?

tomcat雙親委派類型圖

  • commonClassLoader:tomcat最基本的類加載器,加載路徑中的class可以被Tomcat容器本身以及各個WebApp訪問;
  • catalinaClassLoader:tomcat容器私有的類加載器,加載路徑中的class對于WebApp不可見。
  • sharedClassLoader:各個WebApp共享的類加載器,加載路徑中的class對于所有WebApp可見,但是對于Tomcat容器不可見
  • WebAppClassLoader:各個WebApp私有的類加載器,加載路徑中的class只對于當前WebApp可見,比如加載war包里的相關類,每個war包應用都有自己的WebAppClassLoader,實現相互隔離,比如不同war包應用引入了不同的spring版本,這樣實現就能加載給咱的spring版本。webappClassLoader加載自己的目錄下的class文件,不會傳遞給父類加載器,打破了雙親委派機制
    委派關系:
  1. CommonClassLoader能加載的類都可以被Catalina ClassLoader和SharedClassLoader使用,從而實現了公有類庫的共用,而CatalinaClassLoader和Shared ClassLoader自己能加載的類則與對方相互隔離。
  2. WebAppClassLoader可以使用SharedClassLoader加載到的類,但WebAppClassLoader實例之間相互隔離。
  3. JasperLoader的加載范圍僅僅是這個JSP文件所編譯出來的那一個.Class文件,它出現的目的就是為了被丟棄:當Web容器檢測到JSP文件被修改時,會替換掉目前的JasperLoader的實例,并通過再建立一個新的Jsp類加載器來實現JSP文件的HotSwap功能。

加載類關系

tomcatclassloader類繼承關系

**注意:**同一個JVM內,兩個相同包名和類名的類對象可以共存,因為他們的類加載器可以不一樣,所以看兩個類對象是否是同一個,除了看類的包名和類名是否都相同之外,還需要他們的類加載器也是同一個才能認為他們是同一個。

課后小問題

1.為什么先執行靜態代碼塊,后執行構造方法?

答:因為在初始化的時候,會執行靜態代碼塊的內容,而構造方法是在new對象的時候調用的。這也正證實加載并不會new一個完整對象出來。而僅僅只會有一個class對象到堆中。

2.為什么要設計雙親委派機制?

答:(1)沙箱安全機制:自己寫的java.lang.String.class類不會被加載,這樣便可以防止核心API庫被隨意篡改。
(2)避免類的重復加載:當父親已經加載了該類時,就沒有必要子Classloader在加載一次,保證被加載的類的唯一性。

3.Tomcat如果使用默認的雙親委派類加載機制行不行? 為什么?

答:不行。這就涉及到Tomcat要解決的問題。

  • 如果使用默認的類加載器機制,那么是無法加載兩個相同類庫的不同版本的,默認的類加載器是不管類的版本,只根據全名查找,且只有一份。
  • 如何實現jsp的熱加載問題,因為jsp會編譯成calss文件,如果修改了jsp,但是類名并沒有變,類加載器會直接取方法區中的已存在的信息,修改的并不會被加載到。所以,Tomcat的處理辦法是,當你修改jsp,他會卸載掉這個jsp的類加載器,重新創建新的類加載器,加載jsp文件。

4.Tomcat打破了類加載機制,是否可以惡意定義HashMap?是否安全

答:不可以,因為上層類加載器依然沒有變,根據列關系圖,都會走到secureClassLoader,做安全校驗。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/76477.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/76477.shtml
英文地址,請注明出處:http://en.pswp.cn/web/76477.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

IP編址(來自YESLAB新網工的筆記)

上層協議類型 概念&#xff1a;通常指的是位于網絡層&#xff08;如 IP 層&#xff09;以上的協議類型&#xff0c;這些協議在數據傳輸時需要由網絡層&#xff08;或更低層&#xff09;協議承載。以 IP 協議為例&#xff0c;IP 報文頭部中的 協議字段&#xff08;Protocol Fie…

SpringBoot學習(過濾器Filter。攔截器Interceptor。全局異常捕獲處理器GlobalExceptionHandler)(詳細使用教程)

目錄 一、過濾器Filter。 1.1定義與規范。 1.2工作原理與范圍。 1.3使用場景。 1.4 SpringBoot實現過濾器。&#xff08;Filter配置2種方式&#xff09; <1>注解配置(WebFilter、Order、ServletComponentScan)。 創建過濾器類。 啟用 Servlet 組件掃描。 <2>配置類…

c++題目_P1443 馬的遍歷

P1443 馬的遍歷 # P1443 馬的遍歷 ## 題目描述 有一個 $n \times m$ 的棋盤&#xff0c;在某個點 $(x, y)$ 上有一個馬&#xff0c;要求你計算出馬到達棋盤上任意一個點最少要走幾步。 ## 輸入格式 輸入只有一行四個整數&#xff0c;分別為 $n, m, x, y$。 ## 輸出格式 …

清華《數據挖掘算法與應用》K-means聚類算法

使用k均值聚類算法對表4.1中的數據進行聚類。代碼參考P281。 創建一個名為 testSet.txt 的文本文件&#xff0c;將以下內容復制粘貼進去保存即可&#xff1a; 0 0 1 2 3 1 8 8 9 10 10 7 表4.1 # -*- coding: utf-8 -*- """ Created on Thu Apr 17 16:59:58 …

HarmonyOS-ArkUI V2工具類:AppStorageV2:應用全局UI狀態存儲

AppStorageV2是一個能夠跨界面存儲數據,管理數據的類。開發者可以使用AppStorageV2來存儲全局UI狀態變量數據。它提供的是應用級的全局共享能力,開發者可以通過connect綁定同一個key,進行跨ability數據共享。 概述 AppStorageV2是一個單例,創建時間是應用UI啟動時。其目的…

打靶日記 zico2: 1

一、探測靶機IP&#xff08;進行信息收集&#xff09; 主機發現 arp-scan -lnmap -sS -sV -T5 -p- 192.168.10.20 -A二、進行目錄枚舉 發現dbadmin目錄下有個test_db.php 進入后發現是一個登錄界面&#xff0c;嘗試弱口令&#xff0c;結果是admin&#xff0c;一試就出 得到加…

使用Java基于Geotools的SLD文件編程式創建與磁盤生成實戰

前言 在地理信息系統&#xff08;GIS&#xff09;領域&#xff0c;地圖的可視化呈現至關重要&#xff0c;而樣式定義語言&#xff08;SLD&#xff09;文件為地圖元素的樣式配置提供了強大的支持。SLD 能夠精確地定義地圖圖層中各類要素&#xff08;如點、線、面、文本等&#x…

kubernetes》》k8s》》Service

Kubernetes 中的 Service 是用于暴露應用服務的核心抽象&#xff0c;為 Pod 提供穩定的訪問入口、負載均衡和服務發現機制。Service在Kubernetes中代表了一組Pod的邏輯集合&#xff0c;通過創建一個Service&#xff0c;可以為一組具有相同功能的容器應用提供一個統一的入口地址…

【HDFS】EC重構過程中的校驗功能:DecodingValidator

一、動機 DecodingValidator是在HDFS-15759中引入的一個用于校驗EC數據重構正確性的組件。 先說下引入DecodingValidator的動機,據很多已知的ISSUE(如HDFS-14768, HDFS-15186, HDFS-15240,這些目前都已經fix了)反饋, EC在重構的時候可能會有各種各樣的問題,導致數據錯誤…

現代c++獲取linux系統架構

現代c獲取linux系統架構 前言一、使用命令獲取系統架構二、使用c代碼獲取系統架構三、驗證四、總結 前言 本文介紹一種使用c獲取linux系統架構的方法。 一、使用命令獲取系統架構 linux系統中可以使用arch或者uname -m命令來獲取當前系統架構&#xff0c;如下圖所示 archuna…

didFinishLaunching 與「主線程首次 idle」, 哪個是更優的啟動結束時間點 ?

結論先行 在這兩個候選時間點里—— application:didFinishLaunchingWithOptions: 執行結束主線程第一次進入 idle&#xff08;RunLoop kCFRunLoopBeforeWaiting&#xff09; 若你只能二選一&#xff0c;以「主線程首次 idle」作為 啟動結束 更合理。它比 didFinishLaunchin…

Vue3 + TypeScript中defineEmits 類型定義解析

TypeScript 中 Vue 3 的 defineEmits 函數的類型定義&#xff0c;用于聲明組件可以觸發的事件。以下是分步解釋&#xff1a; 1. 泛型定義 ts <"closeDialog" | "getApplySampleAndItemX"> 作用&#xff1a;定義允許的事件名稱集合&#xff0c;即組…

樹莓派超全系列教程文檔--(34)樹莓派配置GPIO

配置GPIO GPIO控制gpio 文章來源&#xff1a; http://raspberry.dns8844.cn/documentation 原文網址 GPIO控制 gpio 通過 gpio 指令&#xff0c;可以在啟動時將 GPIO 引腳設置為特定模式和值&#xff0c;而以前需要自定義 dt-blob.bin 文件。每一行都對一組引腳應用相同的設…

AladdinEdu(H卡GPU算力平臺)使用教程: 1)注冊與開通流程 2)插件使用流程

一、注冊與開通流程 首先進入AladdinEdu官網&#xff1a;AladdinEdu-同學們用得起的H卡算力平臺-高效做AI就上Aladdin 完成注冊&#xff0c;并進行學生認證&#xff1a;學生認證賬戶&#xff0c;認證期間享受教育優惠價。 登錄官網進入控制臺 二、插件使用流程 VScode中…

精益數據分析(6/126):深入理解精益分析的核心要點

精益數據分析&#xff08;6/126&#xff09;&#xff1a;深入理解精益分析的核心要點 在創業和數據驅動的時代浪潮中&#xff0c;我們都在不斷探索如何更好地利用數據推動業務發展。我希望通過和大家分享對《精益數據分析》的學習心得&#xff0c;一起在這個充滿挑戰和機遇的領…

2.深入剖析 Rust+Axum 類型安全路由系統

摘要 詳細解讀 RustAxum 路由系統的關鍵設計原理&#xff0c;涵蓋基于 Rust 類型系統的路由匹配機制、動態路徑參數與正則表達式驗證以及嵌套路由與模塊化組織等多種特性。 一、引言 在現代 Web 開發中&#xff0c;路由系統是構建 Web 應用的核心組件之一&#xff0c;它負責…

運籌學之模擬退火

目錄 一、歷史二、精髓思想三、案例與代碼實現 一、歷史 問&#xff1a;誰在什么時候提出模擬退火&#xff1f;答&#xff1a;模擬退火算法&#xff08;Simulated Annealing&#xff0c;SA&#xff09;是由斯圖爾特柯爾斯基&#xff08;Scott Kirkpatrick&#xff09; 等人在 …

android測試依賴

Android 項目中常用的測試相關庫 1. androidx.arch.core:core-testing:2.2.0 作用&#xff1a; 提供與 Android Architecture Components&#xff08;如 LiveData、ViewModel&#xff09;相關的測試工具。主要用于測試基于 LiveData 的異步操作。 常見功能&#xff1a; 即時…

stack,queue和priority_queue

1. stack 1.1 stack 的介紹 棧是一種容器適配器&#xff0c;專門設計用于LIFO環境&#xff08;后進先出&#xff09;&#xff0c;其中元素僅從容器的一端插入和提取。 容器適配器&#xff0c;也就是使用特定容器類的封裝對象作為其底層容器&#xff0c;提供一組特定的成員函…

MinnowBoard MAX單板UEFI BIOS代碼編譯教程

此教程用于UEFI EDK2代碼的研究&#xff0c;雖然EDK2框架代碼開源&#xff0c;但是都是在模擬器上跑仿真&#xff0c;差點意思&#xff0c;搞過嵌入式的應該有一個共識&#xff0c;是騾子是馬&#xff0c;你得把板子點亮啊。MinnowBoard MAX單板是intel10多年前發布的軟硬件全部…