JVM核心學習筆記
文章目錄
1. 類載入全過程
為什麼研究類載入全過程?
– 有助於瞭解JVM執行過程
– 更深入瞭解 java 動態性,(解熱部署、動態載入),提高程式的靈活性。
類載入機制
– JVM 把 class檔案載入到記憶體,並對資料進行校驗、解析和初始化,最終形成 JVM 可以直接使用的 Java 型別的過程。·
1.1 載入
將 class 檔案位元組碼內容載入到記憶體中,並將這些靜態資料轉換成方法區中的執行時資料結構,在堆中生成一個代表這個類的 java.lang.Class 物件,作為方法區類資料的訪問入口。 這個過程需要類載入器參與。
1.2 連結
將Java類的二進位制程式碼合併到JVM的執行狀態之中的過程
(1)驗證:確保載入的類資訊符合JVM規範,沒有安全方面的問題。
(2)準備:正式為類變數(static變數)分配記憶體並設定類變數初始值的階段,這些記憶體都將在方法區中進行分配
(3)解析:虛擬機器常量池內的符號引用替換為直接引用的過程
1.3 初始化
• 初始化階段是執行類構造器()方法的過程。類構造器()方法是由編譯器自動收集
類中的所有類變數的賦值動作和靜態語句塊(static塊)中的語句合併產生的。
• 當初始化一個類的時候,如果發現其父類還沒有進行過初始化、則需要先出發其父類的初始化
• 虛擬機器會保證一個類的()方法在多執行緒環境中被正確加鎖和同步。
類的主動引用(一定會發生類的初始化)
– new一個類的物件
– 呼叫類的靜態成員(除了final常量)和靜態方法
– 使用java.lang.reflect包的方法對類進行反射呼叫
– 當虛擬機器啟動,java Hello,則一定會初始化Hello類。說白了就是先啟動main方法所在的類
– 當初始化一個類,如果其父類沒有被初始化,則先會初始化他的父類
• 類的被動引用(不會發生類的初始化) – 當訪問一個靜態域時,只有真正宣告這個域的類才會被初始化
• 通過子類引用父類的靜態變數,不會導致子類初始化
– 通過陣列定義類引用,不會觸發此類的初始化
– 引用常量不會觸發此類的初始化(常量在編譯階段就存入呼叫類的常量池中了)
測試用例如下
package com.bigdataBC.jvm;
public class demo01 {
static{
System.out.println("靜態初始化Demo01");
}
public static void main(String[] args) throws Exception {
System.out.println("Demo01的main方法!");
System.out.println(System.getProperty("java.class.path"));
//主動引用
// new A();
// System.out.println(A.width);
// Class.forName("com.bjsxt.test.A");
//被動引用
// System.out.println(A.MAX);
// A[] as = new A[10];
System.out.println(B.width);
}
}
class B extends A {
static {
System.out.println("靜態初始化B");
}
}
class A extends A_Father {
public static int width=100; //靜態變數,靜態域 field
public static final int MAX=100;
static {
System.out.println("靜態初始化類A");
width=300;
}
public A(){
System.out.println("建立A類的物件");
}
}
class A_Father extends Object {
static {
System.out.println("靜態初始化A_Father");
}
}
2. 類載入器的作用
類載入器的作用
– 將class檔案位元組碼內容載入到記憶體中,並將這些靜態資料轉換成方法
區中的執行時資料結構,在堆中生成一個代表這個類的java.lang.Class
物件,作為方法區類資料的訪問入口。
類快取
• 標準的Java SE類載入器可以按要求查詢類,但一旦某個類被載入到類載入
器中,它將維持載入(快取)一段時間。不過,JVM垃圾收集器可以回收
這些Class物件。
3. java.class.ClassLoader類
-
作用:
-
java.lang.ClassLoader
類的基本職責就是根據一個指定的類的名稱,
找到或者生成其對應的位元組程式碼,然後從這些位元組程式碼中定義出一個
Java 類,即 java.lang.Class 類的一個例項。 -
除此之外,ClassLoader還負責載入 Java 應用所需的資源,如影像文
件和配置檔案等。
-
-
相關方法
getParent()
返回該類載入器的父類載入器。
loadClass(String name)
載入名稱為 name的類,返回的結果是 java.lang.Class 類的例項。
findClass(String name)
查詢名稱為 name的類,返回的結果是 java.lang.Class類的例項。
findLoadedClass(String name)
查詢名稱為 name的已經被載入過的類,返回的結果是 java.lang.Class類的實
例。
defineClass(String name, byte[] b, int off, int len)
把位元組陣列 b中的內容轉換成 Java 類,返回的結果是
java.lang.Class 類的例項。這個方法被宣告為 final的。resolveClass(Class<?> c) 連結指定的 Java 類。- 對於以上給出的方法,表示類名稱的 name引數的值是類的二進位制名稱。需要注意的是內部類的表示,如
com.example.Sample$1
和com.example.Sample$Inner
等表示方式。
4. 類載入器的層次結構(樹狀結構)
-
引導類載入器(bootstrap class loader)
– 它用來載入 Java 的核心庫(JAVA_HOME/jre/lib/rt.jar
,或sun.boot.class.path
路徑下的
內容),是用原生程式碼來實現的(這個是用C++寫的,後面介紹的三個載入器是用Java寫的),並不繼承自 java.lang.ClassLoader。
– 載入擴充套件類和應用程式類載入器。並指定他們的父類載入器。
-
擴充套件類載入器(extensions class loader)
– 用來載入 Java 的擴充套件庫(JAVA_HOME/jre/ext/*.jar
,或java.ext.dirs路徑下的內容) 。
Java 虛擬機器的實現會提供一個擴充套件庫目錄。該類載入器在此目錄裡面查詢並載入 Java
類。
– 由sun.misc.Launcher$ExtClassLoader實現
- 應用程式類載入器(application class loader)
– 它根據 Java 應用的類路徑(classpath,
java.class.path 路徑下的內容)來載入 Java 類。
一般來說,Java 應用的類都是由它來完成載入的。
– 由sun.misc.Launcher$AppClassLoader實現 - 自定義類載入器
– 開發人員可以通過繼承 java.lang.ClassLoader類的方式
實現自己的類載入器,以滿足一些特殊的需求。
上圖表示的雖然看似是父子關係,其實不是繼承而是組合的關係。
5. 類載入器的代理模式
代理模式
– 交給其他載入器來載入指定的類
• 雙親委託機制
– 就是某個特定的類載入器在接到載入類的請求時,首先將載入任務委
託給父類載入器,依次追溯,直到最高的爺爺輩的,如果父類載入器
可以完成類載入任務,就成功返回;只有父類載入器無法完成此載入
任務時,才自己去載入。
– 雙親委託機制是為了保證 Java 核心庫的型別安全。
• 這種機制就保證不會出現使用者自己能定義java.lang.Object類的情況。
– 類載入器除了用於載入類,也是安全的最基本的屏障。
• 雙親委託機制是代理模式的一種
– 並不是所有的類載入器都採用雙親委託機制。
– tomcat伺服器類載入器也使用代理模式,所不同的是它是首先嚐試去載入某個類,如果找不到再代理給父類載入器。
這與一般類載入器的順序是相反的
6. java.class.ClassLoader類API
• 相關方法
– getParent() 返回該類載入器的父類載入器。
– loadClass(String name) 載入名稱為 name的類,返回的結果是 java.lang.Class類的例項。
• 此方法負責載入指定名字的類,首先會從已載入的類中去尋找,如果沒有找到;從parent ClassLoader[ExtClassLoader]中載入;如
果沒有載入到,則從Bootstrap ClassLoader中嘗試載入(findBootstrapClassOrNull方法), 如果還是載入失敗,則自己載入。如果還
不能載入,則丟擲異常ClassNotFoundException。
• 如果要改變類的載入順序可以覆蓋此方法;
– findClass(String name) 查詢名稱為 name的類,返回的結果是 java.lang.Class類的例項。
– findLoadedClass(String name) 查詢名稱為 name的已經被載入過的類,返回的結果是 java.lang.Class類的實
例。
– defineClass(String name, byte[] b, int off, int len) 把位元組陣列 b中的內容轉換成 Java 類,返回的結果是
java.lang.Class類的例項。這個方法被宣告為 final的。
– resolveClass(Class<?> c) 連結指定的 Java 類。
–
– 表示類名稱的 name引數的值是類的名稱。需要注意的是內部類的表示,如 com.example.Sample
1
和
c
o
m
.
e
x
a
m
p
l
e
.
S
a
m
p
l
e
1和 com.example.Sample
1和com.example.SampleInner等表示方式。
7. 自定義類載入器
• 檔案系統類載入器
• 自定義類載入器的流程:
– 1、首先檢查請求的型別是否已經被這個類裝載器裝載到名稱空間中了,如果已經裝載,直接返回;否則轉入步驟2
– 2、委派類載入請求給父類載入器(更準確的說應該是雙親類載入器,真個虛擬機器中各種類載入器最終會呈現樹狀結構),如果父類加
載器能夠完成,則返回父類載入器載入的Class例項;否則轉入步驟3
– 3、呼叫本類載入器的findClass(…)方法,試圖獲取對應的位元組碼,如果獲取的到,則呼叫defineClass(…)匯入型別到方法區;如
果獲取不到對應的位元組碼或者其他原因失敗,返回異常給loadClass(…), loadClass(…)轉拋異常,終止載入過程(注意:這裡的
異常種類不止一種)。
– 注意:被兩個類載入器載入的同一個類,JVM不認為是相同的類。
7.1 檔案類載入器
先準備一個編譯好的 class 檔案
定義一個檔案類載入器
package com.bigdataBC.loader;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* 自定義檔案系統類載入器
*
*/
public class FileSystemClassLoader extends ClassLoader {
//com.bigdataBC.loader.HelloWorld --> d:/myjava/ com/bigdataBC/loader/HelloWorld.class
private String rootDir;
public FileSystemClassLoader(String rootDir){
this.rootDir = rootDir;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class<?> c = findLoadedClass(name);
//應該要先查詢有沒有載入過這個類。如果已經載入,則直接返回載入好的類。如果沒有,則載入新的類。
if(c!=null){
return c;
}else{
ClassLoader parent = this.getParent();
try {
c = parent.loadClass(name); //委派給父類載入
} catch (Exception e) {
// e.printStackTrace();
}
if(c!=null){
return c;
}else{
byte[] classData = getClassData(name);
if(classData==null){
throw new ClassNotFoundException();
}else{
c = defineClass(name, classData, 0,classData.length);
}
}
}
return c;
}
private byte[] getClassData(String classname){ //com.bigdataBC.loader.HelloWorld --> d:/myjava/ com/bigdataBC/loader/HelloWorld.class
String path = rootDir +"/"+ classname.replace('.', '/')+".class";
// IOUtils,可以使用它將流中的資料轉成位元組陣列
InputStream is = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try{
is = new FileInputStream(path);
byte[] buffer = new byte[1024];
int temp=0;
while((temp=is.read(buffer))!=-1){
baos.write(buffer, 0, temp);
}
return baos.toByteArray();
}catch(Exception e){
e.printStackTrace();
return null;
}finally{
try {
if(is!=null){
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(baos!=null){
baos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
測試類
package com.bigdataBC.loader;
/**
* 測試自定義的FileSystemClassLoader
*
*/
public class demo {
public static void main(String[] args) throws Exception{
FileSystemClassLoader loader = new FileSystemClassLoader("d:/myjava");
FileSystemClassLoader loader2 = new FileSystemClassLoader("d:/myjava");
Class<?> c = loader.loadClass("com.bigdataBC.loader.HelloWorld");
Class<?> c2 = loader.loadClass("com.bigdataBC.loader.HelloWorld");
Class<?> c3 = loader2.loadClass("com.bigdataBC.loader.HelloWorld");
Class<?> c4 = loader2.loadClass("java.lang.String");
Class<?> c5 = loader2.loadClass("com.bigdataBC.loader.demo");
System.out.println(c.hashCode());
System.out.println(c2.hashCode());
System.out.println(c3.hashCode()); //同一個類,被不同的載入器載入,JVM認為也是不相同的類
System.out.println(c4.hashCode());
System.out.println(c3.getClassLoader()); //自定義的類載入器
System.out.println(c4.getClassLoader()); //引導類載入器
System.out.println(c5.getClassLoader()); //系統預設的類載入器
}
}
7.2 網路類載入器
和上面的例子大同小異,無非就是把本地路徑換成URL路徑
package com.bjsxt.test;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
/**
* 網路類載入器
*
*/
public class NetClassLoader extends ClassLoader {
//com.bigdataBC.loader.HelloWorld --> www.xxx.cn/myjava/ com/bigdataBC/loader/HelloWorld.class
private String rootUrl;
public NetClassLoader(String rootUrl){
this.rootUrl = rootUrl;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class<?> c = findLoadedClass(name);
//應該要先查詢有沒有載入過這個類。如果已經載入,則直接返回載入好的類。如果沒有,則載入新的類。
if(c!=null){
return c;
}else{
ClassLoader parent = this.getParent();
try {
c = parent.loadClass(name); //委派給父類載入
} catch (Exception e) {
// e.printStackTrace();
}
if(c!=null){
return c;
}else{
byte[] classData = getClassData(name);
if(classData==null){
throw new ClassNotFoundException();
}else{
c = defineClass(name, classData, 0,classData.length);
}
}
}
return c;
}
private byte[] getClassData(String classname){ //com.bigdataBC.loader.HelloWorld --> www.xxx.cn/myjava/ com/bigdataBC/loader/HelloWorld.class
String path = rootUrl +"/"+ classname.replace('.', '/')+".class";
// IOUtils,可以使用它將流中的資料轉成位元組陣列
InputStream is = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try{
URL url = new URL(path);
is = url.openStream();
byte[] buffer = new byte[1024];
int temp=0;
while((temp=is.read(buffer))!=-1){
baos.write(buffer, 0, temp);
}
return baos.toByteArray();
}catch(Exception e){
e.printStackTrace();
return null;
}finally{
try {
if(is!=null){
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(baos!=null){
baos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
7.3 加密解密類載入器(取反操作,DES對稱加密解密)
一個簡單的加密工具類
package com.bjsxt.test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 加密工具類
*
*/
public class EncrptUtil {
public static void main(String[] args) {
encrpt("d:/myjava/com/bigdataBC/loader/HelloWorld.class", "d:/myjava/temp/HelloWorld.class");
}
public static void encrpt(String src, String dest){
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream(src);
fos = new FileOutputStream(dest);
int temp = -1;
while((temp=fis.read())!=-1){
fos.write(temp^0xff); //取反操作
}
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
if(fis!=null){
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fos!=null){
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
執行該工具類後在 temp 下面生成一個新的加密過的位元組碼檔案。
那麼如何解密呢?再來定義一個與之對應的解密工具類:
package com.bjsxt.test;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* 載入檔案系統中加密後的class位元組碼的類載入器
*
*/
public class DecrptClassLoader extends ClassLoader {
//com.bigdataBC.loader.HelloWorld --> d:/myjava/ com/bigdataBC/loader/HelloWorld.class
private String rootDir;
public DecrptClassLoader(String rootDir){
this.rootDir = rootDir;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class<?> c = findLoadedClass(name);
//應該要先查詢有沒有載入過這個類。如果已經載入,則直接返回載入好的類。如果沒有,則載入新的類。
if(c!=null){
return c;
}else{
ClassLoader parent = this.getParent();
try {
c = parent.loadClass(name); //委派給父類載入
} catch (Exception e) {
// e.printStackTrace();
}
if(c!=null){
return c;
}else{
byte[] classData = getClassData(name);
if(classData==null){
throw new ClassNotFoundException();
}else{
c = defineClass(name, classData, 0,classData.length);
}
}
}
return c;
}
private byte[] getClassData(String classname){ //com.bigdataBC.loader.HelloWorld --> d:/myjava/ com/bigdataBC/loader/HelloWorld.class
String path = rootDir +"/"+ classname.replace('.', '/')+".class";
// IOUtils,可以使用它將流中的資料轉成位元組陣列
InputStream is = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try{
is = new FileInputStream(path);
int temp = -1;
while((temp=is.read())!=-1){
baos.write(temp^0xff); //取反操作,相當於解密
}
return baos.toByteArray();
}catch(Exception e){
e.printStackTrace();
return null;
}finally{
try {
if(is!=null){
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(baos!=null){
baos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package com.bjsxt.test;
/**
* 測試簡單加密解密(取反)操作
*
*/
public class Demo04 {
public static void main(String[] args) throws Exception {
//測試取反操作
// int a = 3; //0000011
// System.out.println(Integer.toBinaryString(a^0xff));
//加密後的class檔案,正常的類載入器無法載入,報classFormatError
// FileSystemClassLoader loader = new FileSystemClassLoader("d:/myjava/temp");
// Class<?> c = loader.loadClass("HelloWorld");
// System.out.println(c);
DecrptClassLoader loader = new DecrptClassLoader("d:/myjava/temp");
Class<?> c = loader.loadClass("com.bigdataBC.loader.HelloWorld");
System.out.println(c);
}
}
8. 執行緒上下文類載入器
• 雙親委託機制以及預設類載入器的問題
– 一般情況下, 保證同一個類中所關聯的其他類都是由當前類的類載入器所載入的.。
比如,ClassA本身在Ext下找到,那麼他裡面new出來的一些類也就只能用Ext去查詢了(不會低一個級別),所以有
些明明App可以找到的,卻找不到了。
– JDBC API,他有實現的driven部分(mysql/sql server),我們的JDBC API都是由Boot或者Ext來載入的,但是
JDBC driver卻是由Ext或者App來載入,那麼就有可能找不到driver了。在Java領域中,其實只要分成這種Api+SPI(
Service Provide Interface,特定廠商提供)的,都會遇到此問題。
– 常見的 SPI 有 JDBC、JCE、JNDI、JAXP 和 JBI 等。這些 SPI 的介面由 Java 核心庫來提供,如 JAXP 的 SPI 介面定
義包含在 javax.xml.parsers 包中。SPI 的介面是 Java 核心庫的一部分,是由引導類載入器來載入的;SPI 實現的
Java 類一般是由系統類載入器來載入的。引導類載入器是無法找到 SPI 的實現類的,因為它只載入 Java 的核心庫
。
• 通常當你需要動態載入資源的時候 , 你至少有三個 ClassLoader 可以選擇 : – 1.系統類載入器或叫作應用類載入器 (system classloader or application classloader) – 2.當前類載入器
– 3.當前執行緒類載入器
• 當前執行緒類載入器是為了拋棄雙親委派載入鏈模式。
– 每個執行緒都有一個關聯的上下文類載入器。如果你使用new Thread()方式生成新的執行緒,新執行緒將繼承其父執行緒的上
下文類載入器。如果程式對執行緒上下文類載入器沒有任何改動的話,程式中所有的執行緒將都使用系統類載入器作為上
下文類載入器。
• Thread.currentThread().getContextClassLoader()
package com.bjsxt.test;
/**
* 執行緒上下文類載入器的測試
*/
public class Demo05 {
public static void main(String[] args) throws Exception {
ClassLoader loader = Demo05.class.getClassLoader();
System.out.println(loader);
ClassLoader loader2 = Thread.currentThread().getContextClassLoader();
System.out.println(loader2);
Thread.currentThread().setContextClassLoader(new FileSystemClassLoader("d:/myjava/"));
System.out.println(Thread.currentThread().getContextClassLoader());
Class<Demo01> c = (Class<Demo01>) Thread.currentThread().getContextClassLoader().loadClass("com.bigdataBC.loader.HelloWorld");
System.out.println(c);
System.out.println(c.getClassLoader());
}
}
9. TOMCAT伺服器的類載入機制
• 一切都是為了安全!
– TOMCAT不能使用系統預設的類載入器。
• 如果TOMCAT跑你的WEB專案使用系統的類載入器那是相當危險的,你可
以直接是無忌憚是作業系統的各個目錄了。
• 對於執行在 Java EE™容器中的 Web 應用來說,類載入器的實現方式與一
般的 Java 應用有所不同。
• 每個 Web 應用都有一個對應的類載入器例項。該類載入器也使用代理模
式(不同於前面說的雙親委託機制),所不同的是它是首先嚐試去載入某個
類,如果找不到再代理給父類載入器。這與一般類載入器的順序是相反的
。但也是為了保證安全,這樣核心庫就不在查詢範圍之內。
• 為了安全TOMCAT需要實現自己的類載入器。
• 我可以限制你只能把類寫在指定的地方,否則我不給你載入!
OSGI原理介紹
• OSGi™是 Java 上的動態模組系統。它為開發人員提供了面向服務和基於元件的運
行環境,並提供標準的方式用來管理軟體的生命週期。
• OSGi 已經被實現和部署在很多產品上,在開源社群也得到了廣泛的支援。Eclipse
就是基於 OSGi 技術來構建的。
• 原理:
– OSGi 中的每個模組(bundle)都包含 Java 包和類。模組可以宣告它所依賴的需要匯入
(import)的其它模組的 Java 包和類(通過 Import-Package),也可以宣告匯出(
export)自己的包和類,供其它模組使用(通過 Export-Package)。也就是說需要能
夠隱藏和共享一個模組中的某些 Java 包和類。這是通過 OSGi 特有的類載入器機制來
實現的。OSGi 中的每個模組都有對應的一個類載入器。它負責載入模組自己包含的
Java 包和類。當它需要載入 Java 核心庫的類時(以 java開頭的包和類),它會代理給
父類載入器(通常是啟動類載入器)來完成。當它需要載入所匯入的 Java 類時,它會
代理給匯出此 Java 類的模組來完成載入。模組也可以顯式的宣告某些 Java 包和類,必
須由父類載入器來載入。只需要設定系統屬性 org.osgi.framework.bootdelegation
的值即可
相關文章
- JVM學習筆記JVM筆記
- JVM學習筆記——初識JVMJVM筆記
- JVM 學習筆記(五)JVM筆記
- 今日學習JVM筆記JVM筆記
- JVM學習筆記-01JVM筆記
- JVM學習筆記之棧區JVM筆記
- Android 學習筆記核心篇Android筆記
- JVM學習筆記(3)---OutOfMemory詳解JVM筆記
- webpack 學習筆記:核心概念(上)Web筆記
- webpack 學習筆記:核心概念(下)Web筆記
- elasticsearch學習筆記一:核心概念Elasticsearch筆記
- JVM學習筆記——自動記憶體管理JVM筆記記憶體
- JVM學習筆記---伺服器,JVM效能監控工具JVM筆記伺服器
- JVM學習筆記——節碼執行引擎JVM筆記
- java學習筆記-4 JVM垃圾回收(GC)Java筆記JVMGC
- jvm學習筆記6:類載入器JVM筆記
- JVM學習筆記——類載入機制JVM筆記
- JVM學習筆記(4)---垃圾收集器JVM筆記
- JVM狂神說視訊學習筆記JVM筆記
- JAVA核心技術學習筆記--反射Java筆記反射
- JVM學習筆記——Class類檔案解讀JVM筆記
- JVM學習筆記五--虛擬機器棧JVM筆記虛擬機
- 【Linux】核心學習筆記(一)——程序管理Linux筆記
- Spring Boot學習筆記:Spring Boot核心配置Spring Boot筆記
- JVM記憶體分配機制與回收策略選擇-JVM學習筆記(2)JVM記憶體筆記
- Linux核心學習筆記(5)– 程式排程概述Linux筆記
- 【JVM學習筆記】垃圾收集器與記憶體分配策略JVM筆記記憶體
- freeRTOS核心學習筆記(1)-程式設計標準筆記程式設計
- Go語言核心36講(新年彩蛋)--學習筆記Go筆記
- Go語言核心36講(導讀)--學習筆記Go筆記
- JAVA學習筆記—JAVA WEB(二)JAVA WEB核心(下)Java筆記Web
- 筆記:JVM筆記JVM
- numpy的學習筆記\pandas學習筆記筆記
- 學習筆記筆記
- jvm筆記1JVM筆記
- 物件回收判定與垃圾回收演算法-JVM學習筆記(1)物件演算法JVM筆記
- Dubbo學習筆記(三) RPC核心原理和執行緒模型筆記RPC執行緒模型
- 《Java核心技術》第五章 繼承 學習筆記Java繼承筆記