《Java特種兵》學習筆記java
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");
複製代碼
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
StringBuilder stringBuilder = new StringBuilder();
for (...) {
//最壞的狀況是它所佔用的內存空間接近Old區域1/3時發生擴容,致使OOM
stringBuilder.append(string);
}
複製代碼
stringBuilder在append擴容的時候,取max(2倍,count+string.length)
,因此在小字符串append大字符串時,擴容的空間剛剛足夠,而大字符串append小字符串時,就會多出大量的內存空間算法
舉例: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_final
sql
類型 | Cache範圍 |
---|---|
Integer | -128 ~127 |
Short | -128 ~127 |
Long | -128 ~127 |
Float double | 無 |
Byte | 256個值 |
存儲局部變量中的基本數據類型,新建對象時,儲存對象引用,而對象是在堆中建立
Jvm發出指令請求,OS完成具體計算,jvm自身沒法作計算數據庫
一般以連續64位字節爲單位進行cache的
如數組獲取,當取二維數組a[0][0]時,cache line操做一般會將一些臨近的數組元素cache到CPU緩存中,故而連續訪問a[0][0],a[0][1]…時,這些連續的數值只要cache一次數組
同一份數據cache在多個cpu中時,要求數據讀寫一致,多個CPU之間要遵循緩存共享的一致性協議
CPU讀寫數據時都會廣播,其餘CPU監聽,並保存數據一致性緩存
全部程序中使用的地址都是虛擬地址(邏輯地址),在不一樣的進程中能夠重複
物理地址:每一個進程有一段內存區域,起始地址+邏輯地址=物理地址
OS預先給jvm分配-xms大小的內存空間,而不是當即分配一個-xmx大小的空間,許多空間是真正使用時才分配的(啓動java時,-xmx設置比物理內存大均可以)安全
每秒讀取的次數IOPS越大越好
順序讀寫,減小定位延遲
Java中的日誌讀寫工具,會將日誌替換爲buffer的append方式,將日誌寫入一個緩衝區,由專門的程序實現寫操做,或者只在緩衝區已滿的時候寫入磁盤,儘可能一次寫入多條數據,順序IO,減小硬盤尋道尋址
類常量池
方法:編譯時會自動生成構造方法字節碼
略過先
ClassLoader.loadClass(「類名」)
時,會先從當前ClassLoader查找該類是否已經加載,而後逐步往父類查找類,最後由父類往子類加載類,最後ClassNotFoundExceptionBootStrapClassLoader -> ExtClassLoader -> AppClassLoader -> 自定義ClassLoader
java.Lang.*( Object, Class, Number, Thread, System, Throwable…)
,由jvm內核實現,不能被替換掉全部類在使用前都必須被加載和初始化,初始化過程由<clinit>
方法確保線程安全,若多個線程同時嘗試獲取該類,則必須等到static塊執行完成
(BootStrapClassLoader -> ExtClassLoader -> AppClassLoader ->自定義ClassLoader->classnotfoundexception)
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");
}
}
複製代碼
-XX:ReservedCodeCacheSize
: 修改codeCache大小,64bit server java7默認48M-XX:+UseCodeCacheFlushing
: 清理codeCache-XX:CICompilerCount
: 最大並行編譯數,越大提升編譯速度instance.getClass().getResource("").getPath();
得到class來源jar包見jvm筆記
跟 Java 堆大小相關的 JVM 內存參數
參數 | 含義 |
---|---|
-Xms | 設置 Java 堆的初始化大小 |
-Xmx | 設置最大的 Java 堆大小 |
-Xss | 設置Java線程堆棧大小 |
-Xmn | 設置新生代空間大小 |
關於打印垃圾收集器詳情的 JVM 參數
參數 | 含義 |
---|---|
-verbose:gc | 記錄 GC 運行以及運行時間,通常用來查看 GC 是不是應用的瓶頸 |
-XX:+PrintGCDetails | 記錄 GC 運行時的詳細數據信息,包括新生成對象的佔用內存大小以及耗費時間等 |
-XX:-PrintGCTimeStamps | 打印垃圾收集的時間戳 |
設置 Java 垃圾收集器行爲的 JVM 參數
參數 | 含義 |
---|---|
-XX:+UseParallelGC | 使用並行垃圾收集 |
-XX:-UseConcMarkSweepGC | 使用併發標誌掃描收集 |
-XX:-UseSerialGC | 使用串行垃圾收集 |
JVM調試參數,用於遠程調試
-Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000
複製代碼
-Xbootclasspath
用來指定須要加載,但不想經過校驗的類路徑。JVM 會對全部的類在加載前進行校驗併爲每一個類經過一個int數值來應用。這個是保證 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.
複製代碼
-XX:+TraceClassLoading
和 -XX:+TraceClassUnloading
用來打印類被加載和卸載的過程信息,這個用來診斷應用的內存泄漏問題很是有用。-XX:+PrintCompilation
: prints out the name of each Java method Hotspot decides to JIT compile.參數 | 含義 |
---|---|
-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. |
Java對象將以8字節對齊在內存中,不足則補齊
靜態引用所佔的空間一般不計算到對象空間自己的空間上,它的引用在方法區
//32bit
class A{
byte b1;
}
複製代碼
8字節頭部+1字節b1
要對齊,故16字節
class A{byte b;}
class B extends A{byte b;}
class C extends B{byte b;}
複製代碼
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)
}
複製代碼
維度 | 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 |
java.lang.OutOfMemoryError: Java heap space
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
while (true) {
list.add("內存溢出了");
}
}
複製代碼
java.lang.OutOfMemoryError: PermGen space
int i = 0;
while (true) {
("在JDK 1.6下運行,在JDK 1.7中運行的結果將徹底不一樣 "
+ "string常量在jdk1.7以上就再也不存放在PermGen中" + i++).intern();
}
複製代碼
java.lang.OutOfMemoryError: Direct buffer memory
// -XX:MaxDirectMemorySize=26m
public static void main(String[] args) {
ByteBuffer.allocateDirect(27 * 1024 * 1024);
}
複製代碼
public void testStackOver() {
testStackOver();
}
複製代碼
注意遞歸層數,將遞歸調用次數做爲參數,到達必定次數後結束遞歸若字符編碼和解碼的方式不一致,極可能損壞源字符,形成沒法正確讀取
好比變長的UTF-8編碼能夠由3個字節組成一個漢字,而GBK由2個字節組成漢字,GBK按2個單位 長度讀取時,不在其編碼範圍內的則用?或其餘字符替代,這就修改了原來的字符串了
不關閉流會有什麼問題?內存溢出吧
select * from table small,table big where small.id =big.id and small.type=’1’;
複製代碼
small.id =big.id
:用小表作驅動表,更快update table set … where a=xx and b=xxxx
複製代碼
若a有索引,而b沒有,則可能會鎖住全部a條件篩選出的數據,鎖的範圍更大 有些數據庫爲了加快加鎖的速度,會以」塊」爲單位加鎖,塊頭部有內部的行信息,每一行都有1bit來標識是否已經加鎖索引方法:B+樹,也有hash
索引是與數據分開儲存的
索引的目的是快速定位數據,索引經過某種標識符與原表關聯起來(也叫回表),標識符能夠是主鍵,或者創建索引的字段值,也能夠是數據物理位置(oracle用rowid作標識符:表空間編號+文件編號+對象編號+塊號+塊內行號
)
除非查詢的信息所有在索引上,不然索引至少會走兩次操做(索引+回表),因此小規模數據索引表現很差
索引更新時,索引先刪除再插入,其中刪除是僞刪除,索引空間不會變小
從新編譯索引纔會真正刪除索引
索引管理
樹狀管理索引,數據量越大,層數越多,索引間有序,底層全部的葉子塊經過雙向鏈表互相關聯
字段有索引並不必定要走索引
in檢索時,一般解析爲OR方式完成,檢索在多個離散塊中進行,每一個條件都須要單獨查找,in條件多就走全表掃描(將in改成exists?)
再如,某些狀態字段做爲條件,可能也不會走索引
位圖索引bitMap
但字段只有幾種值時,bitmap能夠實現高效的統計.
位圖索引結構相似於B+樹,儲存時以字段值類型分開,在每種值的空間中單獨儲存每一個數據行是否有這個值,1有,0沒有
如字段類型爲A,索引中大體能夠儲存爲101010100,表示第1 3 5 7行有A值
缺點:鎖粒度太大
若修改狀態1爲2,則會鎖住全部值爲1和2的行,commit或rollback纔會釋放
主庫更新數據,再同步到從庫
小表作驅動表,大表走索引
explain plan for sql,select * from table(DBMS_XPLAN.DISPLAY)
SET AUTOTRACE ON EXPLAIN , SET AUTOTRACE ON, SET AUTOTRACE TRACEONLY STAT
explain plan for select * from tableAA a where a.tt=:vv1;
:開頭表明佔位符Mysql
explan<sql>
執行計劃
在JOIN表以前,須要被JOIN的表能先過濾掉大部分數據
小表驅動大表,嵌套循環時,有序會快一些
多個結果集JOIN,可使用Hash Join的方式,當表太大,Hash Join key太多,內存會放不下,用普通嵌套方式作
函數轉換
函數轉換一般不屬於執行計劃的範疇
select fun1(a),fun(b)… from table
執行路徑是看不到函數的處理
表示樹狀結構,如省,市,區縣,鎮,鄉,村,門牌,多級結構,可使用經常使用的遞歸方式,只保留父級id 也能夠按序保留下全部的父級id,避免遞歸,索引也方便
Thread.currentThread().getStackTrace();
複製代碼
new Exception().printStackTrace();
複製代碼
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);
複製代碼
面向切面,動態代理和字節碼加強技術
註解根本上仍是依靠反射實現的
驅動只需加載一次,不須要反覆加載,也不須要本身new,註冊的driver以列表的形式保存,驅動加載也與classloader有關,全部也能夠在不一樣的classloader加載同一驅動的不一樣版本 當程序中存在多個驅動時,DriverManager經過遍歷的方式查找其classloader中全部的driver,與jdbcurl匹配,匹配上就返回