> **程序分析**、**程序生成**和**程序轉換**都是非常有用的技術,可在許多應用環境下使用;ASM 用于**運行時**類生成與轉換(處理**經過編譯**的 Java 類)。
[TOC]
## 1 概述
- **使用范圍**:嚴格限制于類的讀、寫、轉換和分析 (類的加載過程就超出了它的范圍之外)
- **API 模型**:ASM 提供了兩個用于生成和轉換已編譯類的 API,一個是 core API,以基于**事件**的形式來表示類,另一個是 tree API,以基于**對象**的形式來表示類
- core API 要快于 tree API,因為不需要在內存中創建和存儲`Object Tree`
- 但是使用 core API 時,類轉換的實現要更難一些,因為在任意給定時刻,類中只有一個元素可用,tree API 則可以在內存中獲取整個類
- **體系結構**:
- 基于事件的 API 圍繞事件生成器(類分析器)、事件使用器(類寫入器)和各種預定義的事件篩選器進行,可由開發者自定義,分為兩個步驟:
- 將事件生成器(ClassReader)、篩選器(ClassVisitor)和使用器(ClassWriter)組裝為體系結構
- 啟動事件生成器(ClassReader),以執行生成或轉換過程
- 基于對象的 API 中,用于操作 Object Tree 的類生成器或轉換器組件是可以通過組裝而形成的,它們之間的鏈接代表著轉換的順序
- **組織形式**:
- `org.objectweb.asm` 和 `org.objectweb.asm.signature` 定義了基于事件的 API,并提供了類分析器和寫入器組件 (asm.jar)
- `org.objectweb.asm.util` 提供了各種基于 core API 的工具,可以在開發和調試 ASM 應用程序時使用 (asm-util.jar)
- `org.objectweb.asm.commons` 提供了幾個很有用的預定義類轉換器,大多基于 core API (asm-commons.jar)
- `org.objectweb.asm.tree` 定義了基于對象的 API,并提供了一些工具,用于在基于事件和基于對象的表示方法之間進行轉換 (asm-tree.jar)
- `org.objectweb.asm.tree.analysis` 以 tree API 為基礎,提供了一個類分析框架和幾個預定義的類分析器 (asm-analysis.jar)
## 2 已編譯類的結構表示
一個**已編譯類**和**源文件類**有以下幾點區別:
- 已編譯類僅描述**一個類**,一個源文件中可以包含幾個類
- 已編譯類**不包含注釋(comment)**,但可以包含類、字段方法和代碼屬性(Attribute),Java 5 引入注解(annotaion)后,屬性已經變得沒有什么用處了
- 已編譯類中不包含 **package** 和 **import**,因此所有類型(Type)都是全限定的(Qualified)
- 已編譯類中包含 [常量池(constant pool)](http://wiki.baidu.com/pages/viewpage.action?pageId=1285072081),其中包含了在類中出現的所有數值、字符串、和類型常量 (但 ASM 隱藏了與常量池有關的所有細節)
### 2.1 內部名 (InternalName)
類型(Type)只能是類(class)或接口類型(interface),類型在已編譯類中用**內部名(InternalName)** 表示;一個類的內部名就是這個類的完全限定名,`.` 號用 `/` 表示,例如 **String** 的內部名為 `java/lang/String`
### 2.2 類型描述符 (Type Descriptor)
內部名只能用于表示類或接口類型,所有其他類型,在已編譯類中都是用類型描述符(Type Descriptor)表示,如 `表2.2.1` 所示。
<blockquote id="表2.2.1">表2.2.1 - 類型描述符示例</blockquote>
Java 類型 | 類型描述符 | 補充說明
---- | ----
boolean | Z |
char | C |
byte | B |
short | S |
int | I |
float | F |
long | J |
double | D |
Object | Ljava/lang/Object; | 用 `L**;` 表示一個類,以 `;` 號結尾
int[] | [I | 數組類型的描述符是一個 `[` 號后加上其**組件類型**的描述符
Object[][] | [[Ljava/lang/Obejct; |
### 2.3 方法描述符(Method Descriptor)
方法描述符是一個類型描述符列表,用一個字符串描述一個方法的參數類型(parameter Type)和返回類型(return Type),如 `表2` 所示。
<blockquote id="表2.2.2">表2.2.2 - 方法描述符示例</blockquote>
源文件中的方法聲明 | 方法描述符
---- | ----
void m(int i, float f) | (IF)V
int m(Object o) | (Ljava/lang/Object;)I
int[] m (int i, String s) | (ILjava/lang/String;)[I
Object m(int[] arr) | ([I)Ljava/lang/Object;
## 3 與已編譯類相關的 ASM API
ASM 提供了三個基于 ClassVisitor API 的核心組件,用于生成和轉換類:
- **ClassReader** 可以看作一個事件產生器,可以分析 `byte[]` 形式的已編譯類,并調用 accept 方法入參里 ClassVisitor 實例的 visitXxx 方法 (`void ClassReader#accept(ClassVisitor, Attribute[], int)`)
- **ClassWriter** 可以看作一個事件使用器,其直接以二進制形式生成已編譯類 (`byte[] ClassWriter#toByteArray()`)
- **ClassVistor** 可以看作一個事件篩選器,可以將它收到的所有**方法調用**都委托給另一個 ClassVisitor 類
### 3.1 ClassVisitor:類的生成與轉換
用于生成和轉換已編譯類的 ASM API 是基于 **ClassVisitor** 抽象類的:
<blockquote id="清單3.1.1">清單3.1.1 - ClassVisitor 源碼</blockquote>
```java
public abstract class ClassVisitor {
protected final int api;
protected ClassVisitor cv;
public ClassVisitor(final int api) {...}
public ClassVisitor(final int api, final ClassVisitor cv) {...}
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {...}
public void visitSource(String source, String debug) {...}
public void visitOuterClass(String owner, String name, String desc) {...}
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {...}
public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {...}
public void visitAttribute(Attribute attr) {...}
public void visitInnerClass(String name, String outerName, String innerName, int access) {...}
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {...}
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {...}
public void visitEnd() {...}
}
```
**ClassVisitor 類的方法必須按以下順序調用:**
1. 首先調用 visit
2. 然后是對 visitSource 的最多一次調用
3. 接下來是對 visitOuterClass 的最多一次調用
4. 然后可按任意順序對 visitAnnotation 和 visitAttribute 的任意多次調用
5. 接著可按任意順序對 visitInnerClass、visitField 或 visitMethod 的任意多次調用
6. 最后是對 visitEnd 的一次調用
<blockquote id="清單3.1.2">清單3.1.2 - ClassVisitor 類方法的訪問順序</blockquote>
```
visit visitSource? visitOuterClass? (visitAnnotation | visitAttribute)* (visitInnerClass | visitField | visitMethod)* visitEnd
```
### 3.2 ClassReader:類的分析
在分析一個已存在的類時,唯一必需的組件是 ClassReader,以下是一個用例。
<blockquote id="清單3.2.1">清單3.2.1 - 一個用于讀取 HashMap 類信息的 ClassReader 示例</blockquote>
```java
public class PrinterClassVisitor extends ClassVisitor {
public static void main(String[] args) throws IOException {
PrinterClassVisitor cv = new PrinterClassVisitor();
ClassReader cr = new ClassReader("java.util.HashMap");
cr.accept(cv, 0);
}
public PrinterClassVisitor() {
super(Opcodes.ASM4);
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
System.out.println(name + " extends " + superName + " {");
}
@Override
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
System.out.println(" " + desc + " " + name);
return null;
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
System.out.println(" " + name + desc);
return null;
}
@Override
public void visitEnd() {
System.out.println("}");
}
}
/* 控制臺輸出:
java/util/HashMap extends java/util/AbstractMap {
J serialVersionUID
I DEFAULT_INITIAL_CAPACITY
...
hash(Ljava/lang/Object;)I
...
internalWriteEntries(Ljava/io/ObjectOutputStream;)V
}
*/
```
### 3.3 ClassWriter:類的生成
為了生成一個類,唯一必需的組件是 ClassWriter,以下是一個用 ClassWriter 生成 A 接口的例子。
> 清單3.3.1 - `pkg.A` 接口
```java
package pkg;
public interface A extends Runnable {
int field1 = -1;
int field2 = 0;
int method1 = (Object o);
}
```
> 清單3.3.1 - 用于生成接口 `pkg/A` 的 ClassWriter 用例
```java
public class ClassWriterTest {
public static void main(String[] args) {
int jdkVersion = V1_5;
int accessFlags = ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE;
String name = "pkg/A";
String signature = null;
String superName = "java/lang/Object";
String[] interfaces = new String[]{"java/lang/Runnable"};
ClassWriter cw = new ClassWriter(0);
cw.visit(jdkVersion, accessFlags, name, signature, superName, interfaces);
cw.visitField(ACC_PUBLIC + ACC_STATIC + ACC_FINAL, "field1", "I", null, -1).visitEnd();
cw.visitField(ACC_PUBLIC + ACC_STATIC + ACC_FINAL, "field2", "I", null, 0).visitEnd();
cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "method1", "(Ljava/lang/Object;)I", null, null).visitEnd();
cw.visitEnd();
// 使用生成的類
// cw.toByteArray();
}
}
```
**使用 ClassWriter 生成的類:**
`cw.toByteArray()` 返回的字節數組可以存儲到 `A.class` 文件中,也可以通過**類加載器(ClassLoader)**動態加載之。
> 清單3.3.2 - 通過自定義類加載器使用 `ClassWriter` 生成的類
```java
public class ClassWriterTest {
public static void main(String[] args) {
ClassWriter cw = new ClassWriter(0);
// 生成類 ...
// 使用生成的類
byte[] bytes = cw.toByteArray();
AClassLoader cl = new AClassLoader();
Class classA = cl.defineClass("pkg.A", bytes);
// 使用 classA ...
}
private static class AClassLoader extends ClassLoader {
public Class defineClass(String name, byte[] bytes) {
return defineClass(name, bytes, 0, bytes.length);
}
}
}
```
### 3.4 轉換類 (transfor)
(1) 將 ClassReader 產生的事件轉給 ClassWriter
<blockquote id="清單3.4.1">清單3.4.1</blockquote>
<pre style="font-family: 'Courier New','MONACO'">
byte[] in = readClassByte();
ClassReader cr = new ClassReader(in);
ClassWriter cw = new ClassWriter(0);
cr.accept(cw, 0);
byte[] out = cw.toByteArray();
</pre>
(2) 在 ClassReader 和 ClassWriter 之間引入一個 ClassVisitor
<blockquote id="清單3.4.2">清單3.4.2</blockquote>
<pre style="font-family: 'Courier New','MONACO'">
byte[] in = readClassByte();
ClassReader cr = new ClassReader(in);
ClassWriter cw = new ClassWriter(0);
<b>ClassVisitor cv = newClassVistor(cw);
cr.accept(cv, 0);</b>
byte[] out = cw.toByteArray();
</pre>
(3) 重寫 ClassVisitor 的方法,實現相應的功能
<blockquote id="清單3.4.3">清單3.4.3</blockquote>
<pre style="font-family: 'Courier New','MONACO'">
private static ClassVisitor newClassVistor(ClassWriter cw) {
return new AClassVisitor(cw);
}
private static class AClassVisitor extends ClassVisitor {
public AClassVisitor(ClassVisitor cw) {
super(ASM4, cw);
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(<b>V1_5</b>, access, name, signature, superName, interfaces); // 可以修改版本號
}
}
</pre>
(4) 上面的代碼中,整個 `in` 都被**分析**,并**重新構建**了 `out`,如果**將 in 中不被轉換的部分直接拷貝到 out 中,不對其分析,也不生成相應的事件**,效率會高得多;ASM 會自動為方法執行這一優化:
- 在 ClassReader#accept 中傳入了 ClassVisitor,如果返回的 MethodVisitor 來自一個 ClassWriter,則整個方法的內容將不會被轉換
- 在這種情況下,ClassReader 不會分析這個方法的內容,也不會生成相應的事件,只是復制 ClassWriter 中表示這個方法的字節數組
<blockquote id="清單3.4.4">清單3.4.4</blockquote>
<pre style="font-family: 'Courier New','MONACO'">
byte[] in = readClassByte();
ClassReader cr = new ClassReader(in);
<b>ClassWriter cw = new ClassWriter(cr, 0);</b> //執行這一優化
ClassVisitor cv = newClassVistor(cw);
cr.accept(cv, 0);
byte[] out = cw.toByteArray();
</pre>
### 3.5 移除類成員
在重寫 ClassVisitor 的方法時,**不轉發相應的調用**,可以**移除**相應的類成員(member)。
<blockquote id="清單3.5.1">清單3.5.1 - 一個移除類字段的例子</blockquote>
<pre style="font-family: 'Courier New','MONACO'">
class RemoveMethodAdapter extends ClassVisitor {
<b>private String mName;
private String mDesc;</b>
public RemoveMethodAdapter(ClassVisitor cv, String mName, String mDesc) {
super(ASM4, cv);
this.mName = mName;
this.mDesc = mDesc;
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
<b>if (name.equals(mName) && desc.equals(mDesc)) {
return null; // 不要委托至下一個訪問器,這樣將移除該方法
}</b>
return cv.visitMethod(access, name, desc, signature, exceptions);
}
}
</pre>
### 3.6 增加類成員
在重寫 ClassVisitor 的方法時,**遵循 `2.1` 的規則,多轉發一些調用**,可以**增加**相應的類成員(member)。
例如,如果要向類中添加一個字段,必須在原方法調用之間添加對 visitField 的一個新調用,而且必須將這個新調用放在類適配器的一個訪問方法中(可以在 **visitEnd** 中添加字段,確保字段名稱不會重復),以下是一個在類中添加字段的用例:
<blockquote id="清單3.6.1">清單3.6.1 - 一個增加類字段的例子</blockquote>
<pre style="font-family: 'Courier New','MONACO'">
class AddFieldAdapter extends ClassVisitor {
private int fAcc;
private String fName;
private String fDesc;
private boolean isFieldPresent;
public AddFieldAdapter(ClassVisitor cv, int fAcc, String fName, String fDesc) {
super(ASM4, cv);
this.fAcc = fAcc;
this.fName = fName;
this.fDesc = fDesc;
}
@Override
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
<b>if (name.equals(fName)) {
isFieldPresent = true; // 確保字段不會重復
}</b>
return cv.visitField(access, name, desc, signature, value);
}
@Override
public void visitEnd() {
<b>if (!isFieldPresent) {
FieldVisitor fv = cv.visitField(fAcc, fName, fDesc, null, null);
if (fv != null) { // 一個類訪問器可以在 visitEnd 中返回 null
fv.visitEnd();
}
}</b>
cv.visitEnd();
}
}
</pre>
### 3.7 轉換鏈
將幾個適配器鏈接在一起,可以組成幾個獨立的類轉換,以完成復雜轉換。
<blockquote id="清單3.7.1">清單3.7.1 - 通過編寫一個 ClassVisitor 將接收到的方法調用同時轉發給幾個 ClassVisitor</blockquote>
<pre style="font-family: 'Courier New','MONACO'">
class MultiClassAdapter extends ClassVisitor {
<b>protected ClassVisitor[] cvs;</b>
public MultiClassAdapter(ClassVisitor[] cvs) {
super(ASM4);
this.cvs = cvs;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
<b>for (ClassVisitor cv : cvs) {
cv.visit(version, access, name, signature, superName, interfaces);
}</b>
}
}
</pre>
> 清單3.7.2 - 一個相對復雜的轉換鏈示意圖
<pre style="font-family: 'Courier New','MONACO'">
CR → [ ] [ ] → [ ]
↘ ↗ ↘
[ ] → [ ] → [ ]
CR → [ ] ↗ ↘ ↘
↘ CW
↘ ↗
CR → [ ] → [ ]
</pre>
## 4 已編譯方法的結構表示
> 在已編譯類的內部,方法的代碼存儲為一系列**字節碼**指令,為了生成和轉換類,最根本的辦法就是**要了解這些指令,并理解它們是如何工作的**
### 4.1 執行模式
- Java 代碼在**線程**內部執行
- 每個線程都有自己的執行棧,棧由**幀**組成
- 每個幀表示一個**方法調用**,每一幀包括一個**局部變量**部分和一個**操作數棧**部分
- 局部變量部分與操作數棧部分的大小取決于方法的代碼,在**編譯時計算完成**,并隨字節代碼指令一起存儲在已編譯類中
以下是一個具有 3 幀的執行棧。第 1 幀包含 3 個局部變量,其操作數棧的最大值為 4,其中包含 2 個值;第 2 幀包含 2 個局部變量,操作數棧中有 2 個值;第 3 幀位于執行棧的頂端,包含 4 個局部變量和 2 個操作數。
> 清單4.1.1 - 一個具有 3 幀的執行棧
<pre style="font-family: 'Courier New','MONACO'">
—————————————————— ——————————————— ——————————————————————
| Frame 1 | | Frame 2 | | Frame 3 |
| [L0] [L1] [L2] | | [L0] [L1] | | [L0] [L1] [L2][L3] |
| [V0][V2][][] | | [V0][V2][] | | [V0][V2] |
—————————————————— ——————————————— ——————————————————————
</pre>
- 非靜態(static)方法需要保存 this 引用。對于非靜態方法,在創建一個幀時,會對其初始化提供一個空棧,并用目標對象 `this` 及該方法的參數來初始化其局部變量。例如調用 `a.equals(b)`時,將創建 1 幀,前 2 個局部變量將被初始化為 a 和 b
- long 和 double 需要兩個變量槽。局部變量部分和操作數棧部分的每個**槽(slot)**可以保存 **long 和 double 變量之外的任意 Java 值**。
### 4.2 字節碼指令
**字節碼的構成:**
字節碼指令由標識該指令的**操作碼**和固定數目的**參數**組成。
- 操作碼是一個 unsigned byte
- 參數是靜態值,確定了精確的指令行為,緊跟操作碼之后
**字節碼的分類:**
字節碼可以分為兩類,一類用于在局部變量和操作數棧之間傳送值,一類僅用于操作數棧。
**用于在局部變量和操作數棧之間傳送值的字節碼:**
- 讀取一個局部變量,并將其值壓到操作樹棧中,其參數是局部變量的索引 i(必須讀取):iload, lload, fload, dload, aload
- 從操作數棧中彈出一個值,并將其值存儲在由索引 i 指定的局部變量中:istore, lstore,fstore, dstore, astore
> 注:將一個值存儲在局部變量中,然后再以不同類型加載之,是非法的;但是如果向局部變量中存儲值,而該值不同于該局部變量中存儲的當前值,卻是合法的。例如 `istore 1 aload 1` 序列是非法的。
**僅用于操作數棧的字節碼:**
- 用于處理 **棧** 上的值:`pop` 彈出棧頂部的值;`dup` 壓入頂部棧值的一個副本;`swap` 彈出兩個值,并按逆序壓入之;……
- 在操作數棧壓入一個 **常量** 值:`aconst_null` 壓入 null;`iconst_0` 壓入 int 值 0;`fconst_0` 壓入 0f,`dconst_0` 壓入 0d,`bipush B` 壓入 byte 值 B;`sipush S` 壓入 short 值 S;`ldc CST` 壓入任意 int、float、long、double、String 或 class 常量 CST;……
- 從操作數棧彈出數值,進行**算術邏輯**處理后,將結果壓入棧中:`*add`、`*sub`、`*mul`、`*div`、`*rem` 分別對應于 `+`、`-`、`*`、`/`、`%` 運算,其中 `*` 表示 `i`、`l`、`f` 或 `d`;還有 `<<`、`>>`、`>>>`、`|`、`&`、`^` 運算的對應指令,用于處理 int 和 long 值
- 從棧中彈出一個值,進行**類型轉換**后,將結果壓入棧中:`i2f`、`f2d`、`l2d` 等,將數值由一種類型轉換為另一種類型;`checkcast T` 將一個引用值轉換為類型 T
- 用于創建**對象**、鎖定對象、檢測對象類型等:例如 `new TYPE` 將一個 TYPE 類型的新對象壓入棧中(TYPE 是一個內部名)
- 用于讀寫一個**字段**的值:`getfield OWNER NAME DESC` 彈出一個對象引用,并壓入其 NAME 字段的值;`putfield OWNER NAME DESC` 彈出一個值和對象引用,并將這個值存儲在它的 NAME 字段中;這兩種情況中,對象必須為 OWNER 類型,字段必須為 DESC 類型;`getstatic` 和 `putstatic` 是類似指令,用于靜態字段(static field)。
- 用于調用一個**方法**或構造方法(其彈出值的個數等于其方法參數個數加1(用于目標對象)),并壓回方法調用的結果:`invokevirtual OWNER NAME DESC` 調用在類 OWNER 中定義的 NAME 方法,其方法描述為 DESC;`invokestatic OWNER NAME DESC` 用于調用靜態方法; `invokespecial` 用于私有方法和構造方法;`invokeinterface` 用于接口中定義的方法;`invokedynamic` 用于動態方法調用機制。
- 用于讀寫**數組**的值:`Xaload` 彈出一個索引和一個數組,并壓入此索引處該數組元素的值;`Xastore` 彈出一個值,一個索引和一個數組,并將這個值存儲在數組的這一索引處;這里的 X 可以是 `i`、`l`、`f`、`d`、`a`,`b`、`c`、`s`。
- 用于無條件地或者在某一條件為真時**跳轉**到到一條任意指令,用于編譯 if、for、do、while、break 和 continue、switch、case 等:例如 `ifeq LABEl` 從棧中彈出一個 int 值,如果該值為 0,則跳轉到 LABEL 指定的指令處(否則正常執行下條指令);還有一些跳轉指令,諸如 `ifne`、`ifge`、`tableswitch`、`lookupswitch`
### 4.3 字節碼指令(執行引擎)工作原理
本節基于以下 User 類,介紹字節碼指令的工作原理。
> 清單4.3.1 - 用于演示字節碼指令工作原理的 <code id="清單4.3.1">User</code> 類
```java
public class User {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void checkAndSetAge(int age) {
if (age > 0) {
this.age = age;
} else {
throw new IllegalArgumentException();
}
}
}
```
**User#getAge 說明:**
> 清單4.3.2 - `User#getAge` 方法的字節碼指令
```java
public int getAge();
Code:
0: aload_0
1: getfield #2 // Field age:I
4: ireturn
```
- 第 1 條指令讀取局部變量0,并這個 0 值壓入操作數棧
- 第 2 條指令從棧中彈出這個值,即 `this`,并將對象的 `age` 字段壓入棧中,即 `this.age`
- 第 3 條指令從棧中彈出這個值,并將其返回給調用者
在這個過程中,執行幀的持續狀態如下:
> 清單4.3.3 - `User#getAge` 方法的執行幀的狀態
<pre style="font-family: 'Courier New','MONACO'">
(1) 初始狀態 (2) aload_0 之后 (3) getfield 之后
[this] [this] [this ]
[ ] [this] [this.age]
</pre>
**User#setAge 說明:**
> 清單4.3.4 - `User#setAge` 方法的字節碼指令
```java
public void setAge(int);
Code:
0: aload_0
1: iload_1
2: putfield #2 // Field age:I
5: return
```
- 第 1 條指令將 `this` 壓入操作數棧
- 第 2 條指令壓入局部變量 1,在為這個**方法調用**創建幀期間,以 age 參數初始化該變量
- 第 3 條指令彈出這兩個值,并將 int 值存儲在被引用對象的 age 字段中,即存儲在 `this.age` 中
- 第 4 條指令表示銷毀當前執行幀(編譯后強制生成),并返回調用者
> 清單4.3.5 | `User#setAge` 方法的執行幀的狀態
<pre style="font-family: 'Courier New','MONACO'">
(1) 初始狀態 (2) aload_0 之后 (3) iload_1 之后 (4) putfield 之后
[this][age] [this][age] [this][age] [this][age]
[ ][ ] [this][ ] [this][age] [ ][ ]
</pre>
**User 默認的構造方法說明:**
> 清單4.3.6 - `User#<init>` 方法的字節碼指令
```java
public User();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
```
- 第 1 條指令將 `this` 壓入操作數棧中
- 第 2 條指令從棧中彈出這個值,并調用 `Object#<init>` 方法
- 第 3 條指令返回調用者
**User#checkAndSetAge 方法說明:**
> 清單4.3.7 - `User#<init>` 方法的字節碼指令
```java
public void checkAndSetAge(int);
Code:
0: iload_1
1: ifle 12
4: aload_0
5: iload_1
6: putfield #2 // Field age:I
9: goto 20
12: new #3 // class java/lang/IllegalArgumentException
15: dup
16: invokespecial #4 // Method java/lang/IllegalArgumentException."<init>":()V
19: athrow
20: return
```
- 第 1 條指令將初始化為 age 的局部變量1 壓入操作數棧
- 第 2 條指令(ifle)從棧中彈出 1 個值,并將其與 0 進行比較,如果小于等于(le) 0,則跳轉到程序計數器(pc)為12的指令,否則不做任何事,繼續執行下一條指令
- 第 3 ~ 5 條指令與 `setAge` 方法類似
- 第 6 條指令(goto)表示無條件跳轉到 pc 為 20 的指令,即返回
- 第 7 條指令(new)表示創建一個 IllegalArgumentException 實例,并將其壓入操作數棧
- 第 8 條指令(dup)表示重復這個值
- 第 9 條指令(invokespecial)表示彈出這兩個副本之一,并對其調用構造方法
- 第 10 條指令(athorw)彈出剩下的副本,并將它作為異常拋出(方法異常調用完成),之后不會繼續執行下一條指令
**異常表(Exception table):**
不存在用于捕獲異常的字節碼,而是將一個方法的字節碼與一個**異常表(Exception table)**相關聯,異常表規定了在某方法中一給定部分拋出異常時必須執行的代碼。對于下面的 tryCatchFinallyTest 方法:
> 清單4.3.8 - `tryCatchFinallyTest` 方法,該方法會返回 `300`
```java
public static int tryCatchFinallyTest() {
try {
return 100;
} catch (Exception ex) {
return 200;
} finally {
return 300;
}
}
```
其字節碼指令如下:
> 清單4.3.9 - `tryCatchFinallyTest` 方法的字節碼指令
```java
public static int tryCatchFinallyTest();
Code:
0: bipush 100
2: istore_0
3: sipush 300
6: ireturn
7: astore_0
8: sipush 200
11: istore_1
12: sipush 300
15: ireturn
16: astore_2
17: sipush 300
20: ireturn
Exception table:
from to target type
0 3 7 Class java/lang/Exception
0 3 16 any
7 12 16 any
}
```
- `from=0, to=3, target=7, type=Exception` 表示:對于 pc 為 0 ~ 3 的指令(try 語句塊),如果發生類型為 java/lang/Exception 的異常,將跳轉到 pc=7 處,否則執行 pc=3 之后的 `ireturn`
- `from=0, to=3, target=16, type=any` 表示:執行完 pc 為 0 ~ 3 的指令(try 語句塊)后,無條件跳轉到 pc=16 處的指令(finally 語句塊)
- `from=7, to=12, target=16, type=any` 表示:執行完 pc 為 7 ~ 12 的指令(catch 語句塊)后,無條件跳轉到 pc=16 處的指令(finally 語句塊)
## 5 與已編譯方法相關的 ASM API
ASM 提供了三個基于 MethodVisitor API 的核心組件,用于生成和轉換方法:
- ClassReader 可以看作一個事件產生器,可以分析已編譯方法的內容 (void ClassReader#accept(ClassVisitor, Attribute[], int))
- ClassWriter 可以直接以二進制形式生成已編譯方法 (MethodVisitor ClassWriter#visitMethod(access, name, desc, signature, exceptions))
- MethodVistor 可以看作一個事件篩選器,將它收到的所有方法調用都委托給另一個 MethodVistor 類
### 5.1 MethodVisitor:方法的生成與轉換
用于生成和轉換已編譯方法的 ASM API 是基于 MethodVisitor 抽象類的:
> 清單5.1.1 - MethodVisitor 源碼
```java
public abstract class MethodVisitor {
protected final int api;
protected MethodVisitor mv;
public MethodVisitor(final int api) {}
public MethodVisitor(final int api, final MethodVisitor mv) {}
// Parameters, annotations and non standard attributes
public void visitParameter(String name, int access) {}
public AnnotationVisitor visitAnnotationDefault() {}
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {}
public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {}
public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {}
public void visitAttribute(Attribute attr) {}
public void visitCode() {}
public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {}
// Normal instructions
public void visitInsn(int opcode) {}
public void visitIntInsn(int opcode, int operand) {}
public void visitVarInsn(int opcode, int var) {}
public void visitTypeInsn(int opcode, String type) {}
public void visitFieldInsn(int opcode, String owner, String name, String desc) {}
public void visitMethodInsn(int opcode, String owner, String name, String desc) {}
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {}
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {}
public void visitJumpInsn(int opcode, Label label) {}
public void visitLabel(Label label) {}
// Special instructions
public void visitLdcInsn(Object cst) {}
public void visitIincInsn(int var, int increment) {}
public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {}
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {}
public void visitMultiANewArrayInsn(String desc, int dims) {}
public AnnotationVisitor visitInsnAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {}
// Exceptions table entries, debug information, max stack and max locals
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {}
public AnnotationVisitor visitTryCatchAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {}
public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {}
public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, TypePath typePath, Label[] start, Label[] end, int[] index, String desc, boolean visible) {}
public void visitLineNumber(int line, Label start) {}
public void visitMaxs(int maxStack, int maxLocals) {}
public void visitEnd() {}
}
```
**MethodVisitor 類的方法必須按以下順序調用:**
> 清單5.1.2 - MethodVisitor 類方法的調用順序
```java
visitAnnotationDefault? (visitAnnotation | visitParameterAnnotation | visitAttribute)* (visitCode (visitTryCatchBlock | visitLabel | visitFrame | visitXxxInsn | visitLocalVariable | visitLineNumber)* visitMaxs)? visitEnd
```
- visitCode 和 visitMaxs 方法可用于**檢測方法的字節碼在一個事件序列中的開始與結束**
- visitEnd 必須在最后調用,用于檢測一個方法在一個事件序列中的結束
- 可以將 ClassVisitor 和 MethodVisitor 類合并,生成完整的類
> 清單5.1.3 - 將 ClassVisitor 和 MethodVisitor 類合并,生成完整的類 [書寫格式]
<pre style="font-family: 'Courier New','MONACO'">
PrinterClassVisitor cv = new PrinterClassVisitor();
MethodVisitor mv1 = cv.visitMethod(0, "m1", null, null, null);
mv1.visitCode();
mv1.visitInsn(0);
// ...
mv1.visitMaxs(0, 0);
mv1.visitEnd();
MethodVisitor mv2 = cv.visitMethod(0, "m2", null, null, null);
mv2.visitCode();
mv2.visitInsn(0);
// ...
mv2.visitMaxs(0, 0);
mv2.visitEnd();
cv.visitEnd();
</pre>
> 清單5.1.4 - 將 ClassVisitor 和 <code id="清單5.1.4">MethodVisitor</code> 類合并,生成完整的類 [代碼示例]
<pre style="font-family: 'Courier New','MONACO'">
/**
* 生成默認的構造方法
*
* @param superName 父類名稱
*/
static void generateDefaultConstruct(ClassWriter cw, String superName) {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, MethodName.CONSTRUCTOR, MethodDesc.EMPTY_VOID, null, null);
// 生成構造方法的字節碼指令
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, superName, MethodName.CONSTRUCTOR, MethodDesc.EMPTY_VOID, false);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
/**
* 生成類方法
*/
private static void generateMethod(ClassWriter cw, Class<?> beanClass, boolean usePropertyDescriptor) {
String internalClassName = BeanEnhanceUtils.getInternalName(beanClass.getName());
ClassDesc classDesc = BeanEnhanceUtils.getClassDescription(beanClass, usePropertyDescriptor);
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, MethodName.VALUE, MethodDesc.VALUE, null, null);
mv.visitCode();
// 有屬性,需要調用 getter 方法
if (classDesc.hasField) {
generateMethodWithFields(internalClassName, classDesc, mv);
} else {
generateMethodWithNoField(mv, classDesc, internalClassName);
}
mv.visitEnd();
}
/**
* 生成beanClass對應的增強類的字節流
*
* @param superName 父類
* @param interfaces 接口列表
*/
static byte[] generate(Class<?> beanClass, String superName, String[] interfaces, boolean usePropertyDescriptor) throws Exception {
String beanClassName = beanClass.getName();
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);// 自動計算maxStack
String getterClassName = createGeneratedClassName(beanClassName);
cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, BeanEnhanceUtils.getInternalName(getterClassName), null, superName, interfaces);
<b>generateDefaultConstruct(cw, superName);</b> // 生成默認構造方法
<b>generateMethod(cw, beanClass, usePropertyDescriptor);</b> // 生成GetterClass
cw.visitEnd();
return cw.toByteArray();
}
</pre>
**new ClassWriter(int flag) 選項:**
- flag 為 0 時,不會進行自動計算,必須自行計算幀,局部變量與操作數棧的大小
- flag 為 `ClassWriter#COMPUTE_MAXS` 時(性能降低10%),ASM 將自動計算局部變量與操作數棧部分的大小,還是必須調用 visitMaxs 方法(可以使用任何參數,但是會被忽略并重新計算),必須自行計算棧幀(實例中 generateDefaultConstruct 方法便是自行計算棧幀)
- flag 為 `ClassWriter#COMPUTE_FRAMES` 時(性能降低50%),一切都是自動計算,不再需要調用 visitFrame,但仍然必須調用 visitMaxs 方法(參數將被忽略并重新計算)
### 5.2 方法的生成
針對以下 User 類(拷貝自 [清單4.3.1](#清單4.3.1)):
> 清單5.2.1 - 用于演示 MethodVisitor 方法生成的 User 類
```java
public class User {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void checkAndSetAge(int age) {
if (age > 0) {
this.age = age;
} else {
throw new IllegalArgumentException();
}
}
}
```
**生成 getAge 方法的 MethodVisitor 用例寫法如下:**
> 清單5.2.2 - 用于生成 `User#getAge` 方法的 MethodVisitor 用例
```java
MethodVisitor mv = getMethodVisitor();
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, "pkg/User", "age", "I");
mv.visitInsn(Opcodes.IRETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
```
**生成 checkAndSetAge 方法的 MethodVisitor 用例寫法如下:**
> 清單5.2.3 - 用于生成 `User#checkAndSetAge` 方法的 MethodVisitor 用例
<pre style="font-family: 'Courier New','MONACO'">
MethodVisitor mv = getMethodVisitor(); // public void checkAndSetAge(int);
mv.visitCode(); // Code:
mv.visitVarInsn(Opcodes.ILOAD, 1); // 0: iload_1
Label thenBlockLabel = new Label(); // (pc = 12)
mv.visitJumpInsn(Opcodes.IFLE, thenBlockLabel); // 1: ifle 12
mv.visitVarInsn(Opcodes.ALOAD, 0); // 4: aload_0
mv.visitVarInsn(Opcodes.ILOAD, 1); // 5: iload_1
mv.visitFieldInsn(Opcodes.PUTFIELD,
"User", "age", "I"); // 6: putfield #2 // Field age:I
Label elseBlockLabel = new Label();// (pc = 20)
mv.visitJumpInsn(Opcodes.GOTO, elseBlockLabel); // 9: goto 20
mv.visitLabel(thenBlockLabel);
mv.visitFrame(Opcodes.F_SAME,
0, null, 0, null);
mv.visitTypeInsn(Opcodes.NEW,
"java/lang/IllegalArgumentException"); // 12: new #3 // class java/lang/IllegalArgumentException
mv.visitInsn(Opcodes.DUP); // 15: dup
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, // 16: invokespecial #4 // Method java/lang/IllegalArgumentException."<init>":()V
"java/lang/IllegalArgumentException",
"<init>", "()V");
mv.visitInsn(Opcodes.ATHROW); // 19: athrow
mv.visitLabel(elseBlockLabel);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
mv.visitInsn(Opcodes.RETURN); // 20: return
mv.visitMaxs(2, 2);
mv.visitEnd();
</pre>
## 6 通過 ASM 為模板引擎實現運行時類增強
### 6.1 模板引擎示例
假設存在以下一種使用模板引擎的場景:
1. 開發者創建了 [清單4.3.1](清單4.3.1) 中的 `User` Java 類,如 `清單6.1.1` 所示
2. 開發者在 `user.html` 文件中自定義了數據模板,如 `清單6.1.2` 所示
3. 開發者調用模板引擎,進行數據綁定與結果渲染,如 `清單6.1.3` 所示
4. 模板引擎輸出渲染結果,如 `清單6.1.4` 所示
> 清單6.1.1 - 用于演示模板引擎的 User 類
```java
public class User {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void checkAndSetAge(int age) {
if (age > 0) {
this.age = age;
} else {
throw new IllegalArgumentException();
}
}
}
```
> 清單6.1.2 - `user.html` 文件的內容
```html
<pre>
idUserMap 中 key 為 1 所對應的 User 實例的 age = ${idUserMap[1].age}
userList 中下標為 1 所對應的 User 實例的 age = ${userList[1].age}
變量 min_age = ${min_age}
</pre>
<ul><!-- 遍歷名稱為 "userList" 的列表 -->
<% for (user in userList) { %>
<li>${user.age}</li>
<% } %>
</ul>
<table><!-- 遍歷名稱為 "idUserMap" 的映射 -->
<% for (entry in idUserMap) {%>
<tr>
<td>${entry.key}</td>
<td>${entry.value.age}</td>
</tr>
<% } %>
</table>
```
> 清單6.1.3 - 通過模板引擎進行數據綁定并渲染結果
```java
Template t = gt.getTemplate("/user.html");
User zhangsan = new User(21);
User lisi = new User(22);
User wangwu = new User(23);
Map<Integer, User> idUserMap = new HashMap<>();
idUserMap.put(1, zhangsan);
idUserMap.put(2, lisi);
List<User> userList = Arrays.asList(zhangsan, lisi, wangwu);
t.binding("zhangsan", zhangsan);
t.binding("idUserMap", idUserMap);
t.binding("userList", userList);
t.binding("min_age", 18);
String res = t.render();
System.out.println(res);
```
> 清單6.1.4 - 模板引擎的數據渲染結果
```html
<pre>
idUserMap 中 key 為 1 所對應的 User 實例的 age = 21
userList 中下標為 1 所對應的 User 實例的 age = 22
變量 min_age = 18
</pre>
<ul><!-- 遍歷名稱為 "userList" 的列表 -->
<li>21</li>
<li>22</li>
<li>23</li>
</ul>
<table><!-- 遍歷名稱為 "idUserMap" 的映射 -->
<tr>
<td>1</td>
<td>21</td>
</tr>
<tr>
<td>2</td>
<td>22</td>
</tr>
</table>
```
### 6.2 運行時生成類實例
在 `6.1` 中演示了模板引擎的使用,本文不討論其語法處理(例如處理循環)的實現,而只是介紹模板引擎如何處理 `User` 類。主要分為以下幾個步驟:
1. 將所有支持的類型(List/Map/Array/Model/...)抽象到 `AttributeAccess` 類中(參考`清單6.2.1`),運行時生成 `User$AttributeAccess` 類的 `byte[]`,設為 `code`
2. 運行時通過自定義類加載器加載`code`(參考`清單6.2.2` 與 `清單6.2.3`),定義 `User$AttributeAccess` 類,并返回該類的實例,設為 `user1`
3. 將 `user1` 提供給模板引擎使用
> 清單6.2.1 AttributeAccess 抽象類,封裝了一個獲取對象的屬性的值的方法
```java
public abstract class AttributeAccess implements java.io.Serializable {
public abstract Object value(Object o, Object name);
public void setValue(Object o, Object name, Object value) {
updateValue(o, name, value);
}
// ...
}
```
> 清單6.2.2 自定義類加載器
```java
public class ByteClassLoader extends ClassLoader {
public ByteClassLoader(ClassLoader parent) {
super(parent);
}
public Class<?> defineClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length);
}
public Class<?> findClassByName(String clazzName) {
try {
return getParent().loadClass(clazzName);
} catch (ClassNotFoundException ignored) {
}
return null;
}
}
```
> 清單6.2.3 通過自定義類加載器加載 code
```java
private Object loadContextClassLoader(byte[] code, String className) {
Object obj;
try {
Class<?> enhanceClass = byteContextLoader.findClassByName(className);
if (enhanceClass == null) {
enhanceClass = byteContextLoader.defineClass(className, code);
}
obj = enhanceClass.newInstance();
} catch (Exception ex) {
return null;
}
return obj;
}
```
綜上所述,問題變為: 如何通過 ASM 生成 `User$AttributeAccess` 類
### 6.3 通過 ASM 進行類的生成與轉換
運行時,會通過 ASM 生成 AttributeAccess 的子類 `User$AttributeAccess`,參考 [清單5.1.4](#清單5.1.4) 中的 `generate` 方法,ASM 會為 `User$AttributeAccess` 生成以下方法:
- 無參構造方法
- value 方法,對應實現 `AttributeAccess#value(Object, Object)` 方法
> 清單6.3.1 - `generate` 方法
```java
// 生成 AttributeAccess 對應的增強類的字節數組
generate(beanClass, InternalName.ATTRIBUTE_ACCESS, null, usePropertyDescriptor);
/**
* 生成 beanClass 對應的增強類的字節流
*
* @param superName 父類
* @param interfaces 接口列表
*/
static byte[] generate(Class<?> beanClass, String superName, String[] interfaces, boolean usePropertyDescriptor) throws Exception {
String beanClassName = beanClass.getName();
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);// 自動計算maxStack
String getterClassName = createGeneratedClassName(beanClassName);
cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, BeanEnhanceUtils.getInternalName(getterClassName), null, superName, interfaces);
// 生成默認構造方法
generateDefaultConstruct(cw, superName);
// 生成類方法
generateMethod(cw, beanClass, usePropertyDescriptor);
cw.visitEnd();
return cw.toByteArray();
}
```
并為其實現 `AttributeAccess#value(Object, Object)` 方法,分三種情況:
- User 中沒有 get(String) 或 get(Object) 方法,如 `清單6.3.2` 所示
- User 中包含 get(String) 方法,如 `清單6.3.3` 所示
- User 中包含 get(Object) 方法,如 `清單6.3.4` 所示
> 清單6.3.2 - User 中沒有 get(String) 或 get(Object) 方法時,ASM 生成的 value 方法 [示例代碼]
<pre style="font-family: 'Courier New','MONACO'">
public Object value(Object bean, Object attr) {
String attrStr = attr.toString();
int hash = attrStr.hashCode();
User user = (User) bean;
switch (hash) {
case 1:
return user.getAge();
case 2:
return user.getXxx();
case 3:
if("age".equals(attrStr)){
return user.getAge();
}
if("xxx".equals(attrStr)){
return user.getXxx();
}
}
<b>throw new RuntimeException(ATTRIBUTE_NOT_FOUND, "attribute : " + attrStr);</b>
}
</pre>
> 清單6.3.3 - User 中包含 get(String) 方法時,ASM 生成的 value 方法 [示例代碼]
<pre style="font-family: 'Courier New','MONACO'">
public Object value(Object bean, Object attr) {
String attrStr = attr.toString();
int hash = attrStr.hashCode();
User user = (User) bean;
switch (hash) {
case 1:
return user.getAge();
case 2:
return user.getXxx();
case 3:
if("age".equals(attrStr)){
return user.getAge();
}
if("xxx".equals(attrStr)){
return user.getXxx();
}
}
<b>return user.get(attrStr);</b>
}
</pre>
> 清單6.3.4 - User 中包含 get(Object) 方法時,ASM 生成的 value 方法 [示例代碼]
<pre style="font-family: 'Courier New','MONACO'">
public Object value(Object bean, Object attr) {
String attrStr = attr.toString();
int hash = attrStr.hashCode();
User user = (User) bean;
switch (hash) {
case 1:
return user.getAge();
case 2:
return user.getXxx();
case 3:
if("age".equals(attrStr)){
return user.getAge();
}
if("xxx".equals(attrStr)){
return user.getXxx();
}
}
<b>return user.get(attr);</b>
}
</pre>
至此,`User$AttrubiteAccess` 類的生成與轉換的過程結束,返回的 `byte[]` 將傳給自定義的類加載器。
## 附錄
### 附錄A 元數據的結構表示
> 表A-1 - 類型簽名舉例
Java 類型 | 對應的類型簽名
---- | ----
`List<E>` | `Ljava/util/List<TE;>;`
`List<?>` | `Ljava/util/List<*>;`
`List<? extends Number>` | `Ljava/util/List<+Ljava/lang/Number;>;`
`List<? super Integer>` | `Ljava/util/List<-Ljava/lang/Integer;>;`
`List<List<String>[]>` | `Ljava/util/List<[Ljava/util/List<Ljava/lang/String;>;>;`
`HashMap<K, V>.HashIterator<K>` | `Ljava/util/HashMap<TK;TV;>.HashIterator<TK;>;`
`static <T> Class<? extends T> m (int n)` | `<T:Ljava/lang/Obejct;>(I)Ljava/lang/Class<+TT>;`
> 表A-2 - 類簽名舉例
Java 類 | 對應的類簽名
---- | ----
`C<E> extends List<E>` | `<E:Ljava/lang/Object;>Ljava/util/List<TE;>;`
- 空白目錄
- 精簡版Spring的實現
- 0 前言
- 1 注冊和獲取bean
- 2 抽象工廠實例化bean
- 3 注入bean屬性
- 4 通過XML配置beanFactory
- 5 將bean注入到bean
- 6 加入應用程序上下文
- 7 JDK動態代理實現的方法攔截器
- 8 加入切入點和aspectj
- 9 自動創建AOP代理
- Redis原理
- 1 Redis簡介與構建
- 1.1 什么是Redis
- 1.2 構建Redis
- 1.3 源碼結構
- 2 Redis數據結構與對象
- 2.1 簡單動態字符串
- 2.1.1 sds的結構
- 2.1.2 sds與C字符串的區別
- 2.1.3 sds主要操作的API
- 2.2 雙向鏈表
- 2.2.1 adlist的結構
- 2.2.2 adlist和listNode的API
- 2.3 字典
- 2.3.1 字典的結構
- 2.3.2 哈希算法
- 2.3.3 解決鍵沖突
- 2.3.4 rehash
- 2.3.5 字典的API
- 2.4 跳躍表
- 2.4.1 跳躍表的結構
- 2.4.2 跳躍表的API
- 2.5 整數集合
- 2.5.1 整數集合的結構
- 2.5.2 整數集合的API
- 2.6 壓縮列表
- 2.6.1 壓縮列表的結構
- 2.6.2 壓縮列表結點的結構
- 2.6.3 連鎖更新
- 2.6.4 壓縮列表API
- 2.7 對象
- 2.7.1 類型
- 2.7.2 編碼和底層實現
- 2.7.3 字符串對象
- 2.7.4 列表對象
- 2.7.5 哈希對象
- 2.7.6 集合對象
- 2.7.7 有序集合對象
- 2.7.8 類型檢查與命令多態
- 2.7.9 內存回收
- 2.7.10 對象共享
- 2.7.11 對象空轉時長
- 3 單機數據庫的實現
- 3.1 數據庫
- 3.1.1 服務端中的數據庫
- 3.1.2 切換數據庫
- 3.1.3 數據庫鍵空間
- 3.1.4 過期鍵的處理
- 3.1.5 數據庫通知
- 3.2 RDB持久化
- 操作系統
- 2021-01-08 Linux I/O 操作
- 2021-03-01 Linux 進程控制
- 2021-03-01 Linux 進程通信
- 2021-06-11 Linux 性能優化
- 2021-06-18 性能指標
- 2022-05-05 Android 系統源碼閱讀筆記
- Java基礎
- 2020-07-18 Java 前端編譯與優化
- 2020-07-28 Java 虛擬機類加載機制
- 2020-09-11 Java 語法規則
- 2020-09-28 Java 虛擬機字節碼執行引擎
- 2020-11-09 class 文件結構
- 2020-12-08 Java 內存模型
- 2021-09-06 Java 并發包
- 代碼性能
- 2020-12-03 Java 字符串代碼性能
- 2021-01-02 ASM 運行時增強技術
- 理解Unsafe
- Java 8
- 1 行為參數化
- 1.1 行為參數化的實現原理
- 1.2 Java 8中的行為參數化
- 1.3 行為參數化 - 排序
- 1.4 行為參數化 - 線程
- 1.5 泛型實現的行為參數化
- 1.6 小結
- 2 Lambda表達式
- 2.1 Lambda表達式的組成
- 2.2 函數式接口
- 2.2.1 Predicate
- 2.2.2 Consumer
- 2.2.3 Function
- 2.2.4 函數式接口列表
- 2.3 方法引用
- 2.3.1 方法引用的類別
- 2.3.2 構造函數引用
- 2.4 復合方法
- 2.4.1 Comparator復合
- 2.4.2 Predicate復合
- 2.4.3 Function復合
- 3 流處理
- 3.1 流簡介
- 3.1.1 流的定義
- 3.1.2 流的特點
- 3.2 流操作
- 3.2.1 中間操作
- 3.2.2 終端操作
- 3.3.3 構建流
- 3.3 流API
- 3.3.1 flatMap的用法
- 3.3.2 reduce的用法
- 3.4 collect操作
- 3.4.1 collect示例
- 3.4.2 Collector接口