主頁 > 後端開發 > 類和物件在JVM中是如何存盤的,竟然有一半人回答不上來!

類和物件在JVM中是如何存盤的,竟然有一半人回答不上來!

2020-11-16 13:24:01 後端開發

前言

這篇博客主要來說說類與物件在JVM中是如何存盤的,由于JVM是個非常龐大的課題,所以我會把他分成很多章節來細細闡述,具體的數量還沒有決定,當然這不重要,重點在于是否可以在文章中學到東西,是否對JVM可以有一些更深的理解,當然這也是筆者自己寫文章的初衷,

問題提出

我們在日常作業學習中所使用的Java語言,其最大的特點就是“跨平臺”,我們不用在不同的平臺上編譯兩套不同的機器碼,而可以做到“一次編譯,到處運行”,其跨平臺最重要的一個因素就在于,Java語言并不直接運行在真實機器上,而是有一個虛擬機(即Java Virtual Machine ,JVM)來承載其運行,我們通過javac命令,將.java檔案編譯成為.class檔案,然后通過虛擬機來編譯/解釋執行成對應的平臺硬編碼并執行,使得只要安裝了該虛擬機的平臺,就可以運行java程式,

實際上,現在不光Java可以運行在Java虛擬機上,還有例如Kotlin、Scala、Groovy、Clojure等語言,都采用了這種模式,編譯成為class檔案后,放在Java虛擬機上運行,所以筆者預計在很長的一段時間內,即使Java會過時,但是Java虛擬機也會存在較長的一段時間,

那么就從最開始說起,我們寫程式時,最先進行的操作一定是新建一個類,然后新建一個物件,那么類與物件在JVM中是如何存盤的呢

如何窺探?

在研究這個問題之前,我們必須要看到類和物件在JVM中是以何種狀態存在的,在筆者經過一段時間的學習后,了解了JDK自帶的一款“神器”—HSDB,下面來介紹其基本的一些使用方式,

啟動

首先需要需要復制jdkjrebin目錄下的sawindbg.dll檔案到jrebin目錄下,然后進入jdklib目錄下,使用java -cp .sa-jdi.jar sun.jvm.hotspot.HSDB,即可啟動HSDB:

類和物件在JVM中是如何存盤的,竟然有一半人回答不上來!啟動HSDB

然后我們啟動一個Java專案,讓其保持啟動狀態:

  public class Blog {
      public static void main(String[] args) {
          System.out.println("Hello JVM");
  
          while(true){}
      }
  }

 

在終端中使用jps -l命令,查看運行起來的Java行程的行程號,

類和物件在JVM中是如何存盤的,竟然有一半人回答不上來!jps查看行程

我這里的行程號是720,獲取到行程號之后,點擊HSDB上的File->Attach to HotSpot Process,并輸入行程號:

類和物件在JVM中是如何存盤的,竟然有一半人回答不上來!HSDBAttach

點擊【OK】,即可系結行程,下圖中是這個Java行程中的所有執行緒,

類和物件在JVM中是如何存盤的,竟然有一半人回答不上來!系結行程成功

查看類

我們可以通過這個工具,來看一下我們剛才運行的這個類究竟是以何種形式,存在于JVM中的,

點擊Tools -> Class Browser,然后可以找到Main方法所在類的記憶體地址,可以看到我創建的類的記憶體地址是0x7c0060828

類和物件在JVM中是如何存盤的,竟然有一半人回答不上來!查看類

然后點擊Tools -> Inspector,在右上方輸入記憶體地址,就可以看到這個類的資料了,

類和物件在JVM中是如何存盤的,竟然有一半人回答不上來!查看類資料

到這里我們已經可以看到,我們所創建的類,其在記憶體中的存在形式,實際上是使用一個名為InstanceKlass的類的實體進行存盤的,我們可以得到一個并不是太準確的結論,也算是到目前為止的一個認知,類在JVM中,是被InstanceKlass所描述的,InstanceKlass中包含類的元資料和方法資訊,例如:Java類的繼承資訊成員變數靜態變數成員方法建構式等,JVM可以通過InstanceKlass來反射出Java類的全部結構資訊,

查看物件

在HSDB中,我們找到類的記憶體地址后,通過Inspector可以清楚地看到類在JVM中的一種存在形式,實際上在我們第一次學Java的時候,就聽過一句話:在Java中,萬物皆物件,在JVM看來,不僅Java物件是物件,Java類也是物件,Java方法也是物件,位元組碼常量池皆為物件,

由于JVM是由C++撰寫,所以我們在Java中宣告的所有東西,都可以在由C++撰寫的JVM中以一個物件的方式存在,正如一個Java類是以InstanceKlass的一個實體物件來表示一樣,Java物件也可以使用一個C++物件來表示,我們可以來重復一次上述的程序,來看看Java物件是如何在JVM中進行存盤的,

首先我們需要修改剛才的測驗代碼:

  public class Blog {
      public static void main(String[] args) {
          //在Main方法中新建一個物件
          Blog blog = new Blog();
  
          while(true){}
      }
  }

我們在Main方法中新建了一個Blog物件,然后在HSDB中查看這個物件在JVM中是怎樣的:類和物件在JVM中是如何存盤的,竟然有一半人回答不上來!

找到創建的物件:

類和物件在JVM中是如何存盤的,竟然有一半人回答不上來!Main執行緒堆疊內容類和物件在JVM中是如何存盤的,竟然有一半人回答不上來!找到執行緒堆疊中物件

可以看到在JVM中,物件是以一個名為Oop的物件來描述的,在Oop物件中,有一個_metadata,代表這個物件的類元資料,其中有一個compressed_klass指標,指向的正是我們上文中說的,描述類的元資訊的InstanceKlass

相信在上面一些小小的測驗中,我們應該都有了一些基本的認知,無論是Java中的類,還是物件,在JVM中都是以物件的形式存在的,存放類的InstanceKlass物件,保存了類的元資料,例如父類、方法、成員變數、靜態變數等等,而Oop物件中保存了物件的一些資訊,了解過物件的記憶體分布的同學應該知道一個Java物件中存放有哪些結構,但是這里先賣個關子,這部分內容會在后期文章中單獨敘述,還有一個指向類元資料InstanceKlass的指標,現在應該可以理解萬物皆物件這句話真正的含義了,但如果覺得這就是全部,那就太早了,這其實只是冰山一角,只是開始,

Oop-Klass模型

在上文中我們對Oop和Klass都有了最基本的認識,Oop用于描述物件,Klass用于描述類,而經過筆者更深入的學習中發現,在JVM中,情況絕不止第一節中提到的這么簡單,

在JVM中,并沒有根據Java實體物件直接通過虛擬機映射到新建的C++物件,而是定義了各種Oop-Klass:

  • Oop(ordinary  object  pointer),用來描述物件實體資訊,
  • Klass,用來描述 Java 類,是虛擬機內部Java型別結構的對等體 ,

而剛才我們看到的InstanceKlass,實際上只是Klass的一種,

Oop體系

看到Oop,大家第一反應一定是Object-oriented programming(面向物件程式設計),但是這里的Oop,是值Ordinary Object Pointer,即標準物件指標,它用來表示物件的實體資訊,

在JVM原始碼里,oopsHierarchy.hpp中定義了oop和klass各自的體系,這個是Oop的體系:

  typedef  class oopDesc*                               oop;//所有oops共同基類
  typedef  class   instanceOopDesc*              instanceOop;//Java類實體物件
  typedef  class methodOopDesc*        methodOop;//Java方法物件
  typedef  class constMethodOopDesc*     constMethodOop;//方法中的只讀資訊物件
  typedef  class methodDataOopDesc*      methodDataOop;//方法性能統計物件
  typedef  class   arrayOopDesc*                    arrayOop;//描述陣列
  typedef  class   objArrayOopDesc*              objArrayOop;//描述參考資料型別陣列
  typedef  class   typeArrayOopDesc*             typeArrayOop;//描述基本資料型別陣列
  typedef  class constantPoolOopDesc*   constantPoolOop;//class檔案中的常量池
  typedef  class constantPoolCacheOopDesc*  constantPoolCacheOop;//常量池快取
  typedef  class klassOopDesc*            klassOop;//指向klass實體
  typedef  class markOopDesc*       markOop;//物件頭
  typedef  class compiledICHolderOopDesc* compiledICHolderOop;

 

為了簡化變數名,JVM統一將結尾的Desc去掉,以Oop為結尾命名,

在Oop體系中,分別使用不同的Oop來表示不同的物件,在代碼的注釋中,筆者已經注明了每一種oop分別用于表示什么物件,HotSpot認為用這些模型,便足以描述Java程式的全部內容,

Klass體系

在JVM原始碼里,oopsHierarchy.hpp中定義了oop和klass各自的體系,這個是Klass的體系:

  class                        Klass;//klass家族的基類
  class                InstanceKlass;//虛擬機層面與Java類對等的資料結構
  class          InstanceMirrorKlass;//描述java.lang.Class的實體
  class     InstanceClassLoaderKlass;//描述類加載器的實體
  class             InstanceRefKlass;//描述java.lang.Reference的子類
  class                  MethodKlass;//表示Java類中的方法
  class          ConstantMethodKlass;//描述Java類方法所對應的位元組碼指令資訊的固有屬性
  class                   KlassKlass;//Klass鏈路的末端,在Jdk8已不存在
  class               ConstPoolKlass;//描述位元組碼檔案中常量池的屬性
  class                   ArrayKlass;//描述陣列的資訊,是抽象類,
  class                ObjArrayKlass;//ArrayKlass的子類,描述參考型別的陣列類元資訊
  class               TypeArrayKlass;//ArrayKlass的子類,描述普通配型的陣列類元資訊

 

Klass主要提供一下兩種能力:

  • klass提供一個與 Java 類對等的 C++型別描述,
  • klass提供虛擬機內部的函式分發機制 ,

由于在JVM中,Java類是以Oop和Klass分別進行表示的,所以Klass體系基本和Oop體系相互對應,

或許將兩個維度分開,對于我們真正理解這個體系并不是一件好事,因為畢竟這兩個體系息息相關,所以筆者在這里只是淺嘗輒止地介紹了一下兩個體系的成員,接下來我們就以一個最簡單的案例來一步步了解Oop-Klass體系,順便驗證我們上文中所說的一些內容,根據上文提到的Oop體系和Klass體系內容,我們分別在Main方法中創建幾個物件:

public class Blog {
    private int a = 10;
    private int b = 20;
    public static void main(String[] args) {
        Blog blog = new Blog();
        int[] typeArray = new int[10];
        Integer[] objArray = new Integer[10];
        while(true){}
    }
}

 

按照我們上文的說法,Klass存盤類的元資訊,Oop用于描述物件的實體資訊,而我們都知道創建一個物件JVM一般分為三步,首先是在堆中先分配一片記憶體空間,第二步需要完成物件的初始化,最后將物件的參考指向該記憶體空間,當然這只是比較宏觀的一種說法,而落實到細節中,大概是這樣一個流程:

1.將Java類加載到方法區,加載到方法區的時候實際上就是創建了一個Klass,Klass中保存了這個Java類的所有資訊,例如:變數、方法、父類、介面、構造方法、屬性等,

2.而在完成物件的初始化時,JVM會在堆分配的空間中,創建一個Oop,這個Oop便是我們這個物件實體在記憶體中的對等體,主要存盤這個物件實體的成員變數,其中這個Oop中存在一個指標,指向Klass,通過這個指標,JVM可以在運行期間,獲取這個物件的所有類元資訊,

看到這里可能有人會說,“哎呀這些不過是你說的,但是我們并沒有真正看過啊,你怎么知道你說的這些就是對的呢?”,不急,我們依舊可以使用HSDB來驗證我們的說法,

還是上文的代碼,打開HSDB后,找到我們創建的Blog物件:

類和物件在JVM中是如何存盤的,竟然有一半人回答不上來!驗證Oop內部

可以看到,我們創建的這個物件,其是由Oop所描述,而Oop物件中存在一個指向Klass的指標,指向Klass,并且Oop物件中主要存放了物件實體的成員變數,說明剛才我們的結論是正確的,而在“宏觀說法”中,物件的參考指向該記憶體空間,實際上就是指向這個Oop物件,那么就可以根據這個操作結果,用一張圖來描述出Oop-Klass模型基本的樣子:

類和物件在JVM中是如何存盤的,竟然有一半人回答不上來!Oop-Klass模型圖

而左側Oop物件圖,實際上就是我們平常經常背的一道面試題的來源,Java物件由什么組成:物件頭、實體資料、對齊填充,在這部分內容中,指向klass的指標還存在是否指標壓縮的概念,當然,這不是今天的重點,這部分內容我會在之后的JVM內容中作為單獨一篇文章來描述,

我們接著往下說,剛才我們只是證明了Oop和Klass模型的內部結構,以及Oop-Klass存在的聯系,是通過一個指標關聯的,還有一個東西并沒有得以證明,就是在最初介紹Oop模型和Klass模型時,我們說過其家族的龐大,對于每一種不同型別的類和物件,都由不同的Oop及Klass進行描述,首先修改一下剛才的代碼,使用HSDB來分別查看不同的類和物件,觀察其區別:

public class Blog {
    //基本資料型別
    private int a = 10;
    private int b = 20;
    //基本資料型別陣列
    private int[]aArray = new int[10];
    //參考資料型別陣列
    private Integer[] bArray = new Integer[10];
    //普通物件
    private Map<String,Object> mapObj = new HashMap<>(16);


    public static void main(String[] args) {
        Blog blog = new Blog();
        int[] typeArray = new int[10];
        Integer[] objArray = new Integer[10];
        while(true){}
    }
}

 

HSDB:

  1. 基本資料型別陣列:

    類和物件在JVM中是如何存盤的,竟然有一半人回答不上來!基本資料型別陣列
  2. 參考資料型別陣列:

    類和物件在JVM中是如何存盤的,竟然有一半人回答不上來!參考資料型別陣列
  3. 物件:

    類和物件在JVM中是如何存盤的,竟然有一半人回答不上來!物件

觀察HSDB,不難看出我們在Blog類中創建的三種不同型別的成員屬性:基本資料型別陣列參考資料型別陣列普通物件,都由不同的Oop-Klass模型進行表示,表示方式大致可以用下圖進行描述:

類和物件在JVM中是如何存盤的,竟然有一半人回答不上來!OopKlass表示型別

Oop-Klass模型的簡易理解

在JVM中,使用Oop-Klass模型這種一分為二的模型區描述Java類,但是筆者認為這種叫法并不是特別容易讓人理解,對于初學者來說,什么是Oop,什么是Klass?并沒有一種可以顧名思義的解讀,實際上,無非就是元資料和實體資料進行分離,所以初學者看到這里,不妨可以把他直接理解為data-meta模型,data即oop、而meta即klass,這樣就可以很好地理解Oop-Klass這個概念了,

而實際上,在JVM中,Klass保存元資料這個概念會更好理解一些,如果你看過JVM原始碼,你會發現,實際上在JVM原始碼中Klass正是繼承Metadata類的,

結語

本文帶大家了解了Java的類與物件在JVM中的存在形式,JVM將其一分為二,分為Oop-Klass,分別存盤物件示例資訊及類的元資訊,在整個證明程序中,我們使用了HSDB這個強大的工具,對這一結構進行窺探及證明,

當然,Oop-Klass模型內部是一個龐大的體系,本文只是抓取了日常使用頻次比較高的類以及比較有特點的一些類進行驗證,感興趣的同學可以在線下根據這套方法,自己去驗證其他的一些型別的表示形式,

這是整個JVM專題的第一篇文章,關于JVM的更多內容將會在之后的JVM文章中進行分享,

如果需要提問,歡迎評論區留言~

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

標籤:Java

上一篇:Spring原始碼分析之回圈依賴及解決方案

下一篇:Python學習筆記3:條件控制/回圈陳述句

標籤雲
其他(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)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more