《Java特種兵》學習筆記

《Java特種兵》學習筆記java

1、功底

1.1 編譯期優化

String a = "a" + "b" + 1;
String b = "ab1";
println(a == b);  // true 編譯期優化
複製代碼
String a = "a";
final String c = "a"; 
//a並非一個常量,而是局部變量,字節碼加強技術就能夠修改a的實際賦值
String b = a + "b";	
String d = c + "b";	//final

//1.編譯器不會看方法內部作了什麼,2.遞歸深度不可預測
//3.返回的是對常量引用的拷貝,不是final的,能夠修改
String e = getA() + "b"; 
String compare = "ab";
println(b == compare);	//fasle
println(d == compare);	//true
println(e == compare);	//fasle

private final static String getA() {
	return "a";
}
複製代碼
//String b = a + "b" 的實際編譯效果
StringBuilder temp = new StringBuilder();
String b = temp.append(a).append("b");
複製代碼

1.2 Intern

public native String intern();
與string pool關聯,加鎖尋找string字符串,用equals方法判斷是不是目標字符串
Jdk1.6: string pool在 Perm Gen中
Jdk1.7:string pool在堆中mysql

public static void test3() {
    String a = "a";
    String b = a + "b";	//a是變量,new StringBuilder().append(a).append("b");
    String c = "ab";	//在string pool中新建ab字符串
    String d = new String(b);//新對象
    println(b == c);	//F
    println(c == d);	//F
    println(c == d.intern());	//True:intern:尋找string pool中的ab,返回地址,沒有則建立後返回地址
    println(b.intern() == d.intern());	//True:intern:尋找string pool中的ab,返回地址,沒有則建立後返回地址
}
複製代碼

老年代是否能夠獨自清理?而新生代不清理?web

1.3 StringBuilder

StringBuilder stringBuilder = new StringBuilder();
for (...) {
    //最壞的狀況是它所佔用的內存空間接近Old區域1/3時發生擴容,致使OOM
    stringBuilder.append(string);	
}
複製代碼

stringBuilder在append擴容的時候,取max(2倍,count+string.length),因此在小字符串append大字符串時,擴容的空間剛剛足夠,而大字符串append小字符串時,就會多出大量的內存空間算法

1.4 大量斷定是|否操做

舉例:java字節碼中的類修飾符,如下都是十六進制,只取後四位顯示spring

public:0001
static:0100
final:1000
複製代碼

對數字取&操做,不爲0時就是true
若判斷 public static final, 先取或 public_static_final=public|static|final;判斷(value& public_static_final)== public_static_finalsql

1.5 數據cache

類型 Cache範圍
Integer -128 ~127
Short -128 ~127
Long -128 ~127
Float double
Byte 256個值

2、計算機工做原理

2.1 棧

存儲局部變量中的基本數據類型,新建對象時,儲存對象引用,而對象是在堆中建立
Jvm發出指令請求,OS完成具體計算,jvm自身沒法作計算數據庫

2.2 Cache line

一般以連續64位字節爲單位進行cache的
如數組獲取,當取二維數組a[0][0]時,cache line操做一般會將一些臨近的數組元素cache到CPU緩存中,故而連續訪問a[0][0],a[0][1]…時,這些連續的數值只要cache一次數組

2.3 緩存一致性協議

同一份數據cache在多個cpu中時,要求數據讀寫一致,多個CPU之間要遵循緩存共享的一致性協議
CPU讀寫數據時都會廣播,其餘CPU監聽,並保存數據一致性緩存

2.4 內存

全部程序中使用的地址都是虛擬地址(邏輯地址),在不一樣的進程中能夠重複
物理地址:每一個進程有一段內存區域,起始地址+邏輯地址=物理地址
OS預先給jvm分配-xms大小的內存空間,而不是當即分配一個-xmx大小的空間,許多空間是真正使用時才分配的(啓動java時,-xmx設置比物理內存大均可以)安全

2.5 磁盤

每秒讀取的次數IOPS越大越好
順序讀寫,減小定位延遲
Java中的日誌讀寫工具,會將日誌替換爲buffer的append方式,將日誌寫入一個緩衝區,由專門的程序實現寫操做,或者只在緩衝區已滿的時候寫入磁盤,儘可能一次寫入多條數據,順序IO,減小硬盤尋道尋址

3、JVM

JVM內存管理模型
Old區域更大一些
靜態數據存放於方法區
String對象的intern方法將string對象拷貝到常量池

3.1 類字節碼

類常量池
方法:編譯時會自動生成構造方法字節碼
略過先

3.2 Class字節碼加載器

  1. 繼承關係
    ClassLoader.loadClass(「類名」)時,會先從當前ClassLoader查找該類是否已經加載,而後逐步往父類查找類,最後由父類往子類加載類,最後ClassNotFoundException
    父類先加載類 BootStrapClassLoader -> ExtClassLoader -> AppClassLoader -> 自定義ClassLoader
  2. BootStrapClassLoader
    加載java自帶的核心類,如java.Lang.*( Object, Class, Number, Thread, System, Throwable…),由jvm內核實現,不能被替換掉
  3. ExtClassLoader
    加載 jre/lib/ext目錄下的jar包
    • 擴展
      jre
      C:\Program Files\Java\jre : 用戶運行java程序的jre環境,爲jvm用
      Jdk
      另外安裝的jdk則是開發程序時須要用到的:
      jdk\lib: 包括java開發環境的jar包
      jdk\jre: 開發環境下運行的是 jdk 下的 jre
      jdk\jre\lib: 開發環境中,運行時須要的jar包,如導入的外部jar包
  4. AppClassLoader
    加載classPath下面的內容
  5. 自定義ClassLoader
    加載class,jar文件,甚至其餘文件,加載位置可本地,也可遠程
    自定義的ClassLoader能夠識別到parentClassLoader加載的類,而其餘的ClassLoader加載的類須要從新拼接出classpath做爲參數動態編譯
    若未指定parentClassLoader,則parentClassLoader默認爲調用者類對應的ClassLoader;初始化時能夠設置parentClassLoader爲null
  6. 啓動加載
    啓動時,只加載jvm核心庫(如BootStrapClassLoader)和main方法相關類

3.3 class加載過程

全部類在使用前都必須被加載和初始化,初始化過程由<clinit>方法確保線程安全,若多個線程同時嘗試獲取該類,則必須等到static塊執行完成

  1. 讀取文件(ClassNotFoundException)
    加載.class文件到方法區內部(包含全部的class和static變量,都是程序中惟一的元素) 先從父classloader加載,找不到就子加載器加載,最後拋出異常
    (BootStrapClassLoader -> ExtClassLoader -> AppClassLoader ->自定義ClassLoader->classnotfoundexception)
  2. 連接(NoClassDefFoundError)
    解析校驗字節碼,不符合規範就拋出NoClassDefFoundError
    爲class對象分配內存
  3. 初始化
    調用class對象構造方法,靜態變量,static塊賦值
    初始化順序
    static塊 -> 代碼塊 -> 構造方法
class Parent {
    public Parent() {
	System.out.println("parent constructor init....");	//4
    }
    static {
	System.out.println("parent static block init....");	//1
    }
    {
	System.out.println("parent normal block call....");	//3
    }
}

class Child extends Parent {
    static {
	System.out.println("child static block call....");	//2
    }
    {
	System.out.println("child block call....");		//5
    }
    public Child() {
	System.out.println("child constructor call....");	//6
    }
}
public class LoadObjectStepDemo {
    public static void main(String[] args) {
	new Child();
    }
}
複製代碼
parent static block init....
child static block call....
parent normal block call....
parent constructor init....
child block call....
child constructor call....
複製代碼

錯誤初始化實例ExceptionInInitializerError

class B {
    // 加載類時先調用static塊
    private final static B instance = new B();
    public static B getInstance() {
	return instance;
    }
    public B() {
	instance.test(); // new B()引用instance實例,但又發現這個類未加載完成,instance爲NULL
    }
    public void test() {
	System.out.println("test");
    }
}
複製代碼

3.4 class其餘知識點

  1. 容器跨應用訪問
    在web容器中使用了不一樣的ClassLoader來加載不一樣的delopy(不一樣應用),但能夠跨classLoader互相訪問信息
  2. ClassLoader一個類只加載一個
    同一個ClassLoader一個類只會加載一個,同一個類可能會被不一樣ClassLoader加載,在單例模式時應該考慮這個問題
  3. Full GC釋放class
    Jvm作fullGC時,只有當相應的ClassLoader下全部的Class都沒有實例引用時,能夠釋放ClassLoader及其下全部class
  4. ClassLoader加載
    ClassLoader自己就是class,在沒有ClassLoader時,由jvm內核加載
  5. Class加載與父類
    先加載父類,先初始化父類static方法
  6. JIT運行時優化
    逐步優化,會將優化後的代碼存放在codeCache中
    -XX:ReservedCodeCacheSize : 修改codeCache大小,64bit server java7默認48M
    -XX:+UseCodeCacheFlushing: 清理codeCache
    -XX:CICompilerCount : 最大並行編譯數,越大提升編譯速度
  7. 同名類加載衝突
    同名類出如今不一樣jar包中,可使用instance.getClass().getResource("").getPath();得到class來源jar包
  8. 根引用
    GC時的根引用是本地變量引用,操做數棧引用,PC寄存器,本地方法棧引用,靜態引用等
    即程序運行時棧中的引用+靜態引用列表
  9. 引發Full GC
    1. Old區域滿 || 小於平均晉升空間大小
    2. Perm區域滿:class string
    3. System.gc()
    4. Dump內存

3.5 回收算法

見jvm筆記

3.6 經常使用GC參數

  1. 跟 Java 堆大小相關的 JVM 內存參數

    參數 含義
    -Xms 設置 Java 堆的初始化大小
    -Xmx 設置最大的 Java 堆大小
    -Xss 設置Java線程堆棧大小
    -Xmn 設置新生代空間大小
  2. 關於打印垃圾收集器詳情的 JVM 參數

    參數 含義
    -verbose:gc 記錄 GC 運行以及運行時間,通常用來查看 GC 是不是應用的瓶頸
    -XX:+PrintGCDetails 記錄 GC 運行時的詳細數據信息,包括新生成對象的佔用內存大小以及耗費時間等
    -XX:-PrintGCTimeStamps 打印垃圾收集的時間戳
  3. 設置 Java 垃圾收集器行爲的 JVM 參數

    參數 含義
    -XX:+UseParallelGC 使用並行垃圾收集
    -XX:-UseConcMarkSweepGC 使用併發標誌掃描收集
    -XX:-UseSerialGC 使用串行垃圾收集
  4. JVM調試參數,用於遠程調試

-Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000
複製代碼
  1. 關於類路徑方面的 JVM 參數
    -Xbootclasspath用來指定須要加載,但不想經過校驗的類路徑。JVM 會對全部的類在加載前進行校驗併爲每一個類經過一個int數值來應用。這個是保證 JVM 穩定的必要過程,但比較耗時,若是但願跳過這個過程,就把類經過這個參數來指定。
  2. 用於修改 Perm Gen 大小的 JVM 參數
    下面的這三個參數主要用來解決 JVM 錯誤: java.lang.OutOfMemoryError:Perm Gen Space.
    -XX:PermSize and -XX:MaxPermSize
    -XX:NewRatio=2  Ratio of new/old generation sizes.
    -XX:MaxPermSize=64m     Size of the Permanent Generation.
    複製代碼
  3. 用來跟蹤類加載和卸載的信息
    -XX:+TraceClassLoading-XX:+TraceClassUnloading 用來打印類被加載和卸載的過程信息,這個用來診斷應用的內存泄漏問題很是有用。
  4. JVM switches related to logging
    -XX:+PrintCompilation: prints out the name of each Java method Hotspot decides to JIT compile.
  5. 用於調試目的的 JVM 開關參數
    參數 含義
    -XX:HeapDumpPath=./java_pid.hprof Path to directory or file name for heap dump.
    -XX:-PrintConcurrentLocks Print java.util.concurrent locks in Ctrl-Break thread dump.
    -XX:-PrintCommandLineFlags Print flags that appeared on the command line.

3.7 Java對象內存結構

Java對象將以8字節對齊在內存中,不足則補齊
靜態引用所佔的空間一般不計算到對象空間自己的空間上,它的引用在方法區

對象內存結構

//32bit
class A{
   byte b1;
}
複製代碼

8字節頭部+1字節b1
要對齊,故16字節

  1. 繼承關係的對象屬性排布
    在內部結構中,父類的屬性依然要被分配到相應的子類對象中,這樣才能在程序中經過父類訪問它的屬性 父類的屬性不能和子類混用,它們必須單獨排布在一個地方
class A{byte b;}
class B extends A{byte b;}
class C extends B{byte b;}
複製代碼

對象結構圖

  1. 數組佔用空間實例(32bit)
int size = 100 * 1024 * 1024;
//1
int[] values = new int[size];
for (int i = 0; i < size; i++) {
    values[i] = i;
}
//2
Integer[] valueIntegers = new Integer[size];
for (int i = 0; i < size; i++) {
    valueIntegers[i] = i;  // 自動裝箱了 new Integer(i)
}
複製代碼
  • 對1:
    int[]數組佔用空間
    8字節頭部+4字節描述數組長度+4X100x1024X1024=400MB(int 值4字節)+padding4字節 ≈ 400MB
  • 對2:
    每一個Integer對象佔8字節頭部+4字節int值,又須要對齊,故16字節
    實例對象一共佔用空間16 X 100x1024X1024=1600MB
    Integer[] 數組佔用空間
    8字節頭部+4字節描述數組長度+引用空間4X100x1024X1024=400MB(每一個引用4字節)+padding4字節 ≈ 400MB
    總空間1600+400=2000MB
  1. Int[2][100] PK int[100][2]
    維度 Int[2][100] int[100][2]
    第一維數組 對象頭部 8 8
    第一維 數組長度描述符 4 4
    第一維 引用寬度 2X4=8 100X4=400
    第一維 Padding 4 4
    第一維 合計 24 416
    第二維 對象頭部 8 8
    第二維 數組長度描述符 4 4
    第二維 引用寬度 100X4=400 2X4=8
    第二維 Padding 4 4
    第二維 合計 416 24
    總計 24+2X416=856 416+100X24=2816

3.8 常見OOM

  1. java.lang.OutOfMemoryError: Java heap space
public static void main(String[] args) {
	List<String> list = new ArrayList<String>();
	while (true) {
	    list.add("內存溢出了");
	}
}
複製代碼
  • 解決方法
    1. 對可能存活較久的大對象:object = null
    2. 代碼提速:代碼運行速度提高,縮短對象生命週期
    3. 修改堆大小
  1. java.lang.OutOfMemoryError: PermGen space
    1. jdk1.6 PermGen空間
    int i = 0;
    while (true) {
        ("在JDK 1.6下運行,在JDK 1.7中運行的結果將徹底不一樣 "
        	+ "string常量在jdk1.7以上就再也不存放在PermGen中" + i++).intern();
    }
    複製代碼
    1. 動態加載class,如使用字節碼加強技術,CGlib一直建立加載class
      若須要動態加載類,動態編譯java代碼,最好是有單獨的classLoader,當class被替換時,原來的class能夠被當作垃圾釋放掉
      釋放class的條件是classLoader下的class都沒有活着的對象
  2. DirectBuffer OOM
    java.lang.OutOfMemoryError: Direct buffer memory
    DirectBuffer區域不是java的heap,而是C heap的一部分,一般FULL GC時回收
    // -XX:MaxDirectMemorySize=26m
    public static void main(String[] args) {
    	ByteBuffer.allocateDirect(27 * 1024 * 1024);
    }
    複製代碼
  3. StackOverflowError
    public void testStackOver() {
    	testStackOver();
    }
    複製代碼
    注意遞歸層數,將遞歸調用次數做爲參數,到達必定次數後結束遞歸
    子類和父類相互間調用方法

4、Java通訊

4.1 字符編碼轉換

若字符編碼和解碼的方式不一致,極可能損壞源字符,形成沒法正確讀取
好比變長的UTF-8編碼能夠由3個字節組成一個漢字,而GBK由2個字節組成漢字,GBK按2個單位 長度讀取時,不在其編碼範圍內的則用?或其餘字符替代,這就修改了原來的字符串了

4.2 流繼承嵌套

  1. 繼承
    有部分流的具體實現中會繼承FilterInputStream, FilterInputStream提供InputStream的默認實現.則流只覆寫特定的方法便可
  2. 嵌套
    能夠對同一個stream使用多個stream實現類來嵌套,注意當其中一個實現stream close時,底層基礎stream關閉了,而其餘stream沒有調用其close方法,可能會出錯
    好比多個BufferedInputStream嵌套一個流,關閉其中一個BufferedInputStream後,底層input關閉,但剩餘的其餘BufferedInputStream沒有執行close方法,其buffer的數據不會刷新到磁盤上,形成數據問題

4.3 I/O與內存

  • 文件讀入與讀出  
    文件讀入與讀出

 不關閉流會有什麼問題?內存溢出吧

  • 阻塞與非阻塞 同步與異步
    同步和異步關注的是消息通訊機制,重點在於被調用的結果
    阻塞和非阻塞關注的是程序在等待調用結果(消息,返回值)時的狀態,重點在於程序自身
    1. 同步
      是在發出一個調用時,在沒有獲得結果以前,該調用就不返回。可是一旦調用返回,就獲得返回值了, 由調用者主動等待這個調用的結果
    2. 異步
      調用在發出以後,這個調用就直接返回了,因此沒有返回結果。在調用發出後,被調用者經過狀態、通知來通知調用者,或經過回調函數處理這個調用。
    3. 阻塞
      阻塞調用是指調用結果返回以前,當前線程會被掛起。調用線程只有在獲得結果以後纔會返回。
    4. 非阻塞
      非阻塞調用指在不能馬上獲得結果以前,該調用不會阻塞當前線程,當前線程繼續執行

5、數據庫

5.1 數據庫基本原理

數據庫基本結構層次

  1. 數據塊/頁
    塊,數據存儲單位:4KB,8KB,16KB, 塊也是虛擬出來的,爲數據庫規範,可由數據庫識別
    每一個塊可存儲多條數據,塊都有標識符(不惟一,只有和更高層次的標識符組合才惟一)
    塊內部每條數據一般都有本身的行號
    數據性能瓶頸在於磁盤IOPS上,讀取1KB和1MB的連續數據,耗時相差無幾
    慢緣由:每一個磁盤IO請求都是串行化,每一個IO請求須要磁道尋址,故而慢
    優化:能夠將多個塊的數據連續放在一塊兒組成一組數據
    當連續塊中有部分數據經常使用,部分不經常使用時,不經常使用的數據會被寫回磁盤,則程序再次加載這些塊時,只是加載未加載的部分,這就形成不連續加載,成爲隨機讀
  • 數據字典
    一般以表的方式儲存,記錄數據庫信息,包括每一個塊在文件系統中的偏移量
    數據量少,更新少,一般加載到內存中提供訪問
    數據庫一般會以LRU的算法來管理塊,讓某些訪問多的塊留在內存,或讓訪問少的塊從內存釋放掉
  1. 分組extends
    將多個塊組成一個分組,理論上是連續儲存的
  2. segments
    多個extends組成一個segments,一個表對象能夠劃分到多個segments,以實現分區表獨立管理
  3. 修改表
    添加一個表格字段
    某些數據庫會事先在寫入每一個塊時預留少部分空間,以便添加字段時使用
    當預留空間的字段不夠用時,會將一行數據的某個部分寫入到另外一個塊中,通常不在連續塊上則須要屢次IO讀取數據了
    某些開源數據庫是沒有數據字典概念,大部分描述信息存放在以表爲單位的文件頭部中,當表結構修改時,一般會建立一個新表,並將原表中的數據所有拷貝到新表中,爲保持數據一致性,拷貝時一般會鎖表
    對於大數據儲存,一般採用分庫分表策略
  4. 刪除表
    僞刪除:刪除某些數據庫中的表,爲了防止誤刪除,會在默認的處理刪除動做時,只是修改一個名字並將修改後的表名與原來的表名作一個簡單映射,同時也改變其元數據的狀態
  5. SQL執行
    解析得到關聯結構信息,執行計劃等
    • 軟解析
      緩存SQL解析過程,一樣SQL,只有參數不一樣,能夠直接拿解析好的結果直接運行,提升效率
    • 硬解析
      拼接處參數,而非預編譯,SQL不復用,低效
    • SQL儲存空間
      SQL存儲空間有限,使用類LRU算法管理內存區域
    • 執行計劃
      單表是否走索引,多表時是jion仍是union,排序方法選擇,分鐘策略肯定等
    select * from table small,table big where small.id =big.id and small.type=’1’;
    複製代碼
    small.id =big.id:用小表作驅動表,更快
    對於過濾性好的條件,能夠查看執行計劃,讓該條件先執行(Oracle和mysql執行順序不一致)
  6. 加鎖
    讀寫表時,一般會在數據字典上加鎖,保證表結構不會被修改,通常相似於讀寫鎖,不過寫時卻也能夠讀,可能會形成讀髒數據的問題,解決方法是增長時間戳或版本號作標識
    對於某些數據庫,鎖住的行可能並不只僅是最終where條件篩選出的行,多是where條件中有索引字段篩選出的全部行
    update table setwhere a=xx and b=xxxx
    複製代碼
    若a有索引,而b沒有,則可能會鎖住全部a條件篩選出的數據,鎖的範圍更大 有些數據庫爲了加快加鎖的速度,會以」塊」爲單位加鎖,塊頭部有內部的行信息,每一行都有1bit來標識是否已經加鎖
    對應大量的數據修改,可能因鎖而致使串行速度問題,能夠利用cas機制代替鎖
  7. 提交
    Commit操做時,會將日誌寫入到磁盤
    數據庫會在內存分配一個日誌緩衝區,隔必定時間/日誌達到必定容量就存盤
    日誌的存在能夠爲數據庫提供回滾功能
    另外,也有多是數據庫記錄每個塊的相應版本到某個空間,只要空間夠用,就不會將相應版本的數據清空,回滾時直接查詢對應版本號/時間點的數據

5.2 索引原理

索引方法:B+樹,也有hash
索引是與數據分開儲存的
索引的目的是快速定位數據,索引經過某種標識符與原表關聯起來(也叫回表),標識符能夠是主鍵,或者創建索引的字段值,也能夠是數據物理位置(oracle用rowid作標識符:表空間編號+文件編號+對象編號+塊號+塊內行號)
除非查詢的信息所有在索引上,不然索引至少會走兩次操做(索引+回表),因此小規模數據索引表現很差
索引更新時,索引先刪除再插入,其中刪除是僞刪除,索引空間不會變小
從新編譯索引纔會真正刪除索引

  • 索引管理
    樹狀管理索引,數據量越大,層數越多,索引間有序,底層全部的葉子塊經過雙向鏈表互相關聯

    三層索引管理
    SQL走索引時,會從樹根開始查找,索引塊一般也cache在緩存中(若索引塊數據量過大,只緩存高層索引塊),可能就不須要I/O,另外,因爲索引有序,能夠經過二分法快速查找,若查完索引後仍須要查詢非索引字段,此時回表,不然直接走索引內部數據
    SQL某些統計操做能夠經過索引完成(好比count),只統計葉子塊,就能夠獲得全表信息
    Min,max操做,有where條件,則正常遍歷索引,沒有條件,直接查第一個和最後一個塊
    like前綴索引能夠經過範圍查找,然後綴索引就多是全表掃描

  • 字段有索引並不必定要走索引
    in檢索時,一般解析爲OR方式完成,檢索在多個離散塊中進行,每一個條件都須要單獨查找,in條件多就走全表掃描(將in改成exists?)
    再如,某些狀態字段做爲條件,可能也不會走索引

  • 位圖索引bitMap
    但字段只有幾種值時,bitmap能夠實現高效的統計.
    位圖索引結構相似於B+樹,儲存時以字段值類型分開,在每種值的空間中單獨儲存每一個數據行是否有這個值,1有,0沒有
    如字段類型爲A,索引中大體能夠儲存爲101010100,表示第1 3 5 7行有A值
    缺點:鎖粒度太大
    若修改狀態1爲2,則會鎖住全部值爲1和2的行,commit或rollback纔會釋放

5.3 數據庫主從原理

主庫更新數據,再同步到從庫

  1. 同步方式:邏輯模式
    主庫執行SQL,到從庫上執行相同SQL
    主庫能夠優化SQL,如生成基於主鍵更新的SQL,從庫執行會簡單一些
    從庫基本只有一條線程寫,其他讀,壓力小,更高效
    缺點:SQL較多,從庫寫速度更不上主庫,致使不一致
  2. 物理模式
    基於修改的數據塊作複製
    提取比從庫版本號更高的數據塊複製到從庫,不用SQL解析,鎖,調度等問題,更加高效

5.4 執行計劃

小表作驅動表,大表走索引

  • Oracle
  1. explain plan for sql,select * from table(DBMS_XPLAN.DISPLAY)
  2. SET AUTOTRACE ON EXPLAIN , SET AUTOTRACE ON, SET AUTOTRACE TRACEONLY STAT
    eg:
    explain plan for select * from tableAA a where a.tt=:vv1; :開頭表明佔位符
    INDEX UNIQUE SACN:走惟一索引
    INDEX RANCE SCAN:普通索引是走範圍索引的,由於能夠重複
    若是Oracle執行計劃不對或不理想,能夠經過Hint方式告訴Oracle應該如何走索引
  • Mysql
    explan<sql>

  • 執行計劃
    在JOIN表以前,須要被JOIN的表能先過濾掉大部分數據
    小表驅動大表,嵌套循環時,有序會快一些
    多個結果集JOIN,可使用Hash Join的方式,當表太大,Hash Join key太多,內存會放不下,用普通嵌套方式作

  • 函數轉換
    函數轉換一般不屬於執行計劃的範疇
    select fun1(a),fun(b)… from table
    執行路徑是看不到函數的處理

5.5 模型結構優化

表示樹狀結構,如省,市,區縣,鎮,鄉,村,門牌,多級結構,可使用經常使用的遞歸方式,只保留父級id 也能夠按序保留下全部的父級id,避免遞歸,索引也方便

6、源碼基礎

6.1 源碼調用路徑

Thread.currentThread().getStackTrace();
複製代碼
new Exception().printStackTrace();
複製代碼

6.2 反射

Boolean類型的屬性注入

Boolean類型屬性名稱仍是不要加is前綴

class Node {
    private boolean good;
    public boolean isGood() {
	return good;
    }
    public void setGood(boolean good) {
	this.good = good;
    }
}
複製代碼

當使用PropertyDescriptor去獲取屬性讀寫方法時,boolean類型默認都會加上is前綴
若屬性名爲isGood,則默認調用isIsGood方法,這時類中isGood方法就無論用了

反射與範型擦除

List<Integer> list = new ArrayList<Integer>();
Method method = ArrayList.class.getDeclaredMethod("add",Object.class);
method.invoke(list, 7);
method.invoke(list, "dfsd");
method.invoke(list, new A("aa"));
System.out.println(list);
複製代碼
  1. 泛型實參只會在類、字段及方法參數內保存其簽名(便可以得到實際的參數類型),沒法經過反射動態獲取泛型實例的具體實參,好比入參爲範型,方法內部就會擦除爲object。
  2. 須要獲取泛型實參的狀況下,方法有三: ①經過傳遞實參類型
    ②明肯定義泛型實參類型,經過反射獲取簽名
    ③經過匿名類捕獲相關的泛型實參

AOP

面向切面,動態代理和字節碼加強技術

  1. 若是目標對象實現了接口,默認狀況下會採用JDK的動態代理實現AOP
  2. 若是目標對象實現了接口,能夠強制使用CGLIB實現AOP
  3. 若是目標對象沒有實現了接口,必須採用CGLIB庫,spring會自動在JDK動態代理和CGLIB之間轉換
  • 動態代理
    動態代理在生成動態字節碼時,並非經過實現類建立子類的方式,而是經過類所擁有的接口列表來完成,即構造出的類和實現類是沒有關係的
    在構造動態代理時,類必須基於接口實現動態代理,在最終得到動態代理的的實例引用時,也只能用接口來獲取
    本質上仍是將實現類做爲引用傳入到Handler中,仍是會調用實現類的方法,只是包裝了一層
    實現接口的方法,方法內再經過引用調用實現類的方法
    若是在內部方法再調用內部方法,那麼在第二層內部方法中是沒法完成AOP切入的,那已是直接調用了
  • 字節碼加強
    若是在內部方法再調用內部方法,能夠一直完成AOP切入,這是由於字節碼加強是構建子類或者直接修改類的字節碼,如果子類,因爲子類覆寫父類方法,就一直調用被修改的子類方法,能夠一直AOP;如果直接修改字節碼,那就是方法修改了

Annotation

註解根本上仍是依靠反射實現的

7、JDBC

7.1 JDBC註冊

驅動只需加載一次,不須要反覆加載,也不須要本身new,註冊的driver以列表的形式保存,驅動加載也與classloader有關,全部也能夠在不一樣的classloader加載同一驅動的不一樣版本 當程序中存在多個驅動時,DriverManager經過遍歷的方式查找其classloader中全部的driver,與jdbcurl匹配,匹配上就返回

相關文章
相關標籤/搜索