Java 知識筆記 - 類、集合、多線程、IO、JVM(最後一次更新,2019年02月17日)

目錄html

GitHub 項目地址java

Class

內部類、靜態內部類、匿名內部類、局部內部類

  • 內部類須要外部類支持,保存了外部類的引用,初始化 Outer.inner x = outerObj.new inner()
  • 靜態內部類不須要外部類支持,初始化 Outer.inner x = new Outer.inner()
  • 匿名內部類,直接在方法中 new Anonymous() { ... }
  • 局部內部類,在方法中定義了類,包含類的名字,如 void function() { class A {...};}

Collection

Java

Collection

  • 添加、刪除等操做時可選操做,如 Arrays.asList,會產生固定大小的集合,會拋出 UnsupportedOperationException

Set

  • HashSet、TreeSet、LinkedHashSet
  • HashSet、LinkedHashSet 注意須要對其中的元素定義 hashcode()
  • SortedSet 有序集合
  • NavigableSet 可導航集合,擁有 lower 等方法

Queue

  • LinkedList、PriorityQueue,性能無差異
  • DeQue,雙端隊列
  • add 不能插入拋出異常,offer 不會
  • remove 移除空會拋出異常,poll 不會

Map

  • HashMap、TreeMap、LinkedHashMap、WeakHashMap、ConcurrentHashMap、IdentityHashMap
  • LinkedHashMap,遍歷按照插入順序
  • WeakHashMap 弱鍵映射,容許釋放映射所指的對象
  • IdentityHashMap 用 == 進行比較
  • SortedMap,排序 Map

Collections

  • 對 Collection 的操做
  • fill 填充對象數組
  • newSetFromMap 用法:實現 ConcurrentHashSet,newSetFromMap(new ConcurrentHashMap)
  • disjoint 不相交集合
  • checkedXXX 元素會檢查類型
  • synchronizedXXX 同步集合
  • unmodified 不可修改集合
  • rotate 循環向後移動,最後的元素往前移
  • shuffle 亂序
  • sort 排序
  • XXXBinarySearch 二分搜索

Arrays

  • 對數組的操做
  • sort 排序
  • binarySearch 二分查找元素
  • stream 流式處理

System

  • arrayCopy 內存級複製,淺複製

Comparator、Comparable

  • Comparator 比較器,通常類直接實現
  • Comparable 可比較的,通常做爲參數傳入

XXXReference

  • WeakReference System.GC() 就能夠回收
  • SoftReference 內存不足回收
  • PhantomReference 調用 clear 方法纔會清除

Spliterator

  • splitable iterator,可分割迭代器
  • 接口是Java爲了並行遍歷數據源中的元素而設計的迭代器

一些設計原則

  • 將保持不變的事物和會改變的事物分離

正確的 equals 方法

  • 自反性、對稱性、傳遞性、一致性、爲 null 結果爲 false
  • 默認 equals 比較地址

hash

正確的 hashcode

  1. 給 result 賦予某個非 0 常量
  2. 對於每個域,計算出一個散列碼 c
  3. 合併計算, result = result * 37 + c
  4. 返回 result

散列碼 c 計算公式

域類型 計算公式
boolean 0 1
byte char short int (int)
long (int) f >> 32
float Float.floatToIntBits
double (int)Double.doubleToIntBits >> 32
Object hashcode
數組 每一項運用

hash 原理

  • 先用 hashcode 計算,無衝突直接使用獲得的值
  • 有衝突,遍歷衝突所在的 list,equals 計算得出值

HashMap 的性能

  • 負載因子,當前存儲 / 容量,默認 0.75
  • 若是知道須要存儲多少數據,設置合適的容量

快速報錯

  • 當非併發集合進行併發操做時,會快速拋出 ConcurrentModificationException

Concurrent

Java

Description

  • 併發提升在阻塞時的性能
  • 函數式語言處理併發
  • Java 線程機制是搶佔式
  • 併發上下文切換代價高
  • 實現:多線程

Priority

  • 大多數狀況下試圖操做線程優先級是種錯誤
  • 優先級在各個操做系統實現不一樣,可靠的是 MAX_PRIORITY、MIN_PRIORITY、NORM_PRIORITY

KeyWord finally

  • 在設置後臺進程時, 後臺線程的 finally 會不執行

終結任務

  • 當心謹慎

線程狀態

  1. new
  2. runnable
  3. blocked
  4. dead

blocked 觸發條件

  • sleep
  • wait 掛起(notify 再次進入就緒狀態)
  • 等待輸入輸出
  • 試圖 synchronized,被其餘線程鎖住

死鎖

  • 潛伏期長,很難復現

產生條件

  • 互斥
  • 一個資源者持有資源
  • 資源不能夠被搶佔
  • 循環等待

免鎖容器

  • 讀取和寫入同時發生
  • 寫入時複製創建讀取內容副本,讀取操做讀源數組,寫入數據寫副本,完成以後以原子性的操做將副本換入源數組
  • 只能保證最終一致性
  • CopyOnWriteXXX

Implementation

Runnable

  • 定義任務
  • 一般寫成無限循環的形式,除非有條件使得終止

Callable

  • 能夠返回 類型的值,返回 Future 對象
  • Future isDone 能夠檢查任務是否完成
  • get 獲取結果,若是結果爲準備就緒,會阻塞

Thread

  • 能夠繼承實現 start 方法,也能夠利用構造器設置 Runnable 的實現類的實例來執行任務
  • start 啓動任務
  • yield 向線程調度器生命發出已經執行完最重要的任務、能夠切換線程的信號,不能依賴
  • registerNatives 線程註冊了本身,在 run 沒有執行完成以前沒法被垃圾回收器清除
  • sleep 休眠
  • setPriority 設置優先級
  • setDaemon 設置後臺進程
  • join 等待線程結束,能夠設置超時時間
  • setUncaughtExceptionHandler 設置異常處理器
  • interrupted 能夠查詢是否產生中斷,並清除中斷狀態
  • wait 線程進入等待狀態,釋放鎖,能夠從 notify 恢復,建議用 while 而不是 if 執行 wait 操做,由於別的操做可能又讓 wait 條件知足
  • wait 會釋放 synchronized 的鎖
  • notify 喚醒一個等待的線程,保證全部線程 wait 條件相同,在一個類中可能有不少任務,只喚醒當前任務相關的線程
  • notifyAll 喚醒全部等待的線程

FutureTask

  • 可取消
  • 異步計算

ThreadFactory

  • Thread 工廠,生成 Thread

Executor

  • 管理 Thread 對象
  • 接收 ThreadFactory 做爲參數
  • execute 接受參數是實現 Runnable 類的實例,submit 接受參數是 Callable 類的實例
  • shutdown 阻止後續提交任務
  • CachedThreadPool 緩存
  • FixedThreadPool 固定大小
  • SingleThreadExecutor 單線程,多任務排隊

Lock

  • 鎖對象,至關於 synchronized
  • 在 finalize 中 unlock
  • 實現鎖的高級功能,如超時
  • lockInterruptibly 產生中斷
  • 性能好(線性增加),可讀性略低

AtomicXXX

  • 原子操做
  • 鎖更安全一些,Atomic 系列類是爲 java.util.concurrent 服務
  • 樂觀鎖,性能通常很高,但併發量大時,CPU 消耗資源多

ThreadLocal

  • 線程本地存儲

BlockingQueue

  • 阻塞隊列
  • 配合 Enum 使用,組建流程

PipedReader & PipedWriter

  • 管道讀寫同步數據

Semaphore

  • 信號量,容許多個任務同時訪問一個資源

Exchanger

  • 兩個任務交換對象的柵欄

synchronized

  • 設置域爲 private,保證只有方法能夠訪問該字段的值
  • 共享域須要同步
  • 鎖方法:當前對象只有一個線程能訪問該方法,效率低
  • 鎖對象:用於臨界區,鎖方法中的部分代碼片斷,效率高
  • 不具有超時等特性
  • 具備鎖的對象能夠訪問其餘該對象加鎖的方法
  • wait, notify, notifyAll 必須在 synchronized 下使用,若是不這麼使用,可能會丟失 notify()
  • 數據量大性能低,但可讀性好

volatile

  • 當即寫入主存中,全部線程都看獲得,避免緩存影響
  • 保證 long、double 賦值操做的原子性
  • 只有一個值會改變的狀況下使用
  • 不能保證線程安全

InterruptedException

  • 中斷線程
  • 注意清理資源
  • IO、synchronized 不可中斷
  • NIO 提供了新中斷方式

CountDownLatch

  • 同步多個任務使用,首先新建 CountDownWatch 肯定任務大小,各個任務 countDown,再 await 等待其餘任務完成

CyclicBarrier

  • 建立一組任務,並行工做,在全部全部任務完成以前等待
  • 相比於 CountDownLatch,能夠屢次觸發
  • 構造函數參數包括所需任務數、全部任務完成以後執行的操做

DelayedQueue

  • 延遲隊列

PriorityBlockingQueue

  • 優先級隊列

ConcurrentHashMap

  • 寫入機制非寫時複製,比寫時複製快

ScheduledExecutorService

  • 定時器
  • schedule 執行一次
  • scheduleAtFixedRate 屢次執行

ReadWriteLock

  • 讀鎖能夠在沒有寫鎖的時候被多個線程同時持有,寫鎖是獨佔的
  • 多讀少寫性能高

Fork/Join

ForkJoinPool
  • invoke 啓動任務
RecursiveTask
  • 定義子任務
  • invokeAll 調用子任務

TransferQueue

  • 隊列滿時,阻塞生產者

有意思的問題:爲何 System.out.println() 不會被中斷?

《Java 編程思想》提了一句 「System.out.println() 不會被中斷」,疑惑的我去看源碼,恍然大悟。git

public void println(boolean x) {
        synchronized (this) {
            print(x);
            newLine();
        }
    }

System 包的 out 是靜態對象,只有一個實例,在執行 println,鎖住本身,下個線程想用 System.out 的方法,只能等當前操做結束。
這在多線程是個性能天坑。每一個線程若是都有 System.out.println 方法,互相阻塞。能夠參考項目下 com.example.concurrent.number.NumberMain 測試。github

三個線程互相打印

三個線程按順序打印 A、B、C,參見 com.example.concurrent.print.PrinterTest。算法

承諾升級理論

線程組的啓示:繼續錯誤的代價由別人來承擔,而認可錯誤的代價由本身來承擔。shell

線程很簡單?

若是某我的表示線程機制很容易或者很簡單,那麼請確保這我的沒有對你的項目做出重要的決策。若是這我的已經在作了,那麼你就已經陷入麻煩中了。編程

雙檢鎖

雙重檢查,加鎖。雙重檢查防止屢次實例化。數組

  • 多線程下須要加鎖
  • 在方法上加鎖影響性能
  • 因爲 JIT 編譯不肯定性,須要在資源上加上 volatile 防止編譯器優化,致使獲取到的 obj 爲空的問題

正確作法:緩存

class SomeClass {
  private volatile Resource resource = null;
  public Resource getResource() {
    if (resource == null) {
      synchronized {
        if (resource == null)
          resource = new Resource();
      }
    }
    return resource;
  }
}

參考雙重檢查鎖定原理詳解安全

IO

通道、緩衝器

  • 通道是一個獲取數據的通路
  • 緩衝器承載了數據
  • 更接近於底層操做系統的操做機制,性能更高

Java

File

  • 處理文件、目錄

FileNameFilter

  • 過濾文件名

InputStream

  • 字節流
  • 輸入,包括字節數組、字符串、文件、管道、流、其餘數據源,包括 ByteArrayInputStream、StringBufferInputStream、FileInputStream、PipedInputStream、SequenceInputStream(合併兩個 InputStream)
  • FilterInputStream 修改了內部的行爲,或者是返回對象的方式,包括 DataInputStream、BuffedInputStream、LineNumberInputStream、PushbackInputStream
  • DataInputStream 讀取數據,必須經過 DataOutputStream 寫入數據,專用於 JVM 平臺

OutputStream

  • 字節流
  • 輸出,包括 ByteArrayOutputStream、FileOutputStream、PipedOutputStream
  • FilterOutputStream 修改了寫入對象的方式,包括 DataOutputStream、PrintStream、BufferedOutputStream
  • DataOutputStream 寫入數據,必須經過 DataInputStream 讀取數據,專用於 JVM 平臺

Reader

  • 字符流
  • 須要用 InputStreamReader 將 InputStream 轉換
  • 主要是爲了國際化
  • XXXInputStream 對應 XXXReader

Writer

  • 字符流
  • 須要用 OutputStreamReader 將 OutputStream 轉換
  • 主要是爲了國際化
  • XXXOutputStream 對應 XXXWriter

RandomAccessFile

  • 隨機讀取
  • seek 調到文件某一個地方

Scanner

  • 讀取器,傳入 InputStream

System.XXX

  • in 輸入、out 輸出、err 錯誤
  • setXXX 重定向

ProcessBuilder

  • 進程控制

NIO

Channel

  • FileInputStream、FileOutputStream、RandomAccessFile 增長了 Channel
  • 操做 ByteBuffer
  • transferTo、transferFrom 傳輸數據
  • read 讀取數據
  • write 寫入數據
  • tryLock 非阻塞加鎖,lock 阻塞加鎖,position 加鎖位置,limit 結束位置

ByteBuffer

  • 讀取數據的單元
  • wrap 保存字節數據
  • allocate 分配空間
  • limit 讀寫閾值,capacity 容量,position 當前讀寫位置,工做流程
  • 初始狀態,limit = capacity,position = 0
  • 工做時,移動 position,直至 limit
  • flip,limit = position,position = 0,通常是寫了不少數據,從新讀數據,纔會這樣使用
  • clear,limit = capacity,position = 0,寫完數據準備讀會使用
  • rewind,position = 0

JVM

本文基於 JDK 8。

分類

程序計數器(Program Counter Register)

  • 內存空間較小,保存了當前線程執行代碼的位置
  • 線程私有
  • 不會 OOM(Out Of Memory)

虛擬機棧(VM Stack)

  • 保存了對象引用等
  • 線程私有
  • 會 OOM、StackOverflow
  • 指定棧大小,-Xss128k

本地方法棧(Native Method Stack)

  • 與虛擬機棧基本相同,執行的事本地方法
  • 線程私有
  • 會 OOM、StackOverflow
  • 指定棧大小,-Xss128k

堆(Heap)

  • 對象的內容會在堆上分配,包括運行時常量池(Runtime Constant Pool)
  • 線程公有
  • 會 OOM
  • 是 GC 管理的主要區域
  • 用 Xmx、Xms 分別控制最大堆內存、最小堆內存,-Xms20m -Xmx20m,指定 OOM 時 dump,-XX:+HeapDumpOnOutOfMemory -XX:HeapDumpPath=e:/

方法區(Method Area)

  • 存儲了類信息、靜態變量等
  • 線程公有
  • 內存分配在 JVM 之中,會 OOM
  • 俗稱永久代(Permanent Generation,簡稱 Perm Gen)
  • 指定大小,-XX:PermSize=10M -XX:MaxPermSize=10m
  • JDK 7 將永久代轉移到堆中
  • JDK 8 已移除永久代,轉移到 Metaspace

Metaspace

  • 原來的 MethodArea
  • 內存分配在系統內存中,超過系統可用內存,會 OOM
  • 指定大小,-XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m

直接內存

  • 不是虛擬機運行的一部分,DirectByteBuffer 會用,主要是從內存中直接讀取文件
  • 內存分配在系統內存中,超過系統可用內存,會 OOM
  • 指定大小,-XX:MaxDirectMemorySize=10m

內存分配

對象建立流程

  • 遇到 new 關鍵字,查找對應類的符號引用,沒有就進行類加載
  • 分配內存
  • 初始化爲零值
  • 設置對象相應的屬性
  • 執行對象的構造函數

內存分配方式

  • 指針碰撞:計算出內存大小,指針向下移動
  • 空閒列表:維護內存可用部分,進行分配

併發

  • CAS 同步處理:併發大容易產生衝突,下降效率
  • TLAB(Thread Local Allocation Buffer):在線程所處空間中分配內存

內存存儲

  • 對象頭(Header):一部分是自身運行數據,考慮到空間效率,空間大小非固定,主要存儲哈希碼、GC 年代等;另外一部分類型指針,存儲對象類型信息,數組還要存儲數組大小
  • 實例數據(Instance Data):存儲實例數據,通常相同寬度的數據會被分配到一塊兒,父類會在子類以前(CompactField 參數爲 true,子類較窄的也可能插入到父類空隙之中)
  • 對齊填充(Padding):佔位符,對象大小必須是 8 字節的整數倍

訪問定位方式

  • 句柄訪問:棧 reference -> 句柄池 對象實例數據指針 -> 實例池,優勢是移動對象時只須要改實例指針
  • 直接指針訪問: 棧 reference -> 對象實例數據指針(包括實例數據),HotSpot 採用,優勢是訪問速度快(減小了一次指針訪問)

對象存活算法

引用計數法(Reference Counting)

  • 對象包含一個引用計數器,若是有引用該對象的對象,計數器 + 1;引用失效時,計數器 - 1
  • 若是引用計數器爲 0,對象可被回收
  • 簡單,效率高
  • 不能解決循環引用的問題

可達性算法

  • 從 GC roots 開始,根據結點向下搜索,沒搜索到的就能夠被回收
    • stack reference
    • static
    • final
    • native

回收

  • 回收對象:會調用一次 finalize 方法,屢次被回收,僅僅調用一次
  • 回收方法區
    • String Pool 等:和普通對象一致
      • 該類的實例是否被回收
      • ClassLoader 是否被回收
      • Class 對象沒有被引用

回收算法

標記-清除(Mark-Sweep)

  • 標記爲可回收的,某一時刻統一回收
  • 標記和清除效率不高
  • 會產生大量的內存碎片,分配大對象時,內存不足

複製(Copying)

  • 空間對半分,只用一塊;一塊用完時,將存活的對象複製到另一塊,按順序劃份內存
  • 不會產生磁盤碎片
  • 內存空間代價較高,一半是空閒的
  • 適合存活率較低的空間

改進

因爲 98% 的對象都是朝生夕死的,不須要對半分空間。

  • 首先分紅三個空間,一塊 Eden、兩塊 Survivor,比例 8:1:1,只使用一塊 Eden、一塊 Survivor
  • 回收時,將 Eden 和 Survivor 存活的對象複製到另外一塊 Survivor
  • 只有 10% 內存會浪費

標記-複製(Mark-Compact)

  • 和標記-清楚相似,最後不清理內存,直接將活的對象複製
  • 適合存活率較高的空間

分代收集(Generational Collection)

  • 新生代:每次垃圾手機都會有大量對象死去,適合使用複製算法
  • 老年代:存活率高,沒有額外空間可使用,適合使用標記-清除或標記複製算法
  • Eden:新生代空間的一部分
  • Survivor: 分爲 from、to,爲新生代空間一部分,GC 收集 Eden 和 from,轉移非垃圾到 to 中或者老年代空間中

分代緣由

新生代存活率低,老年代存活率高,不一樣的特色決定了不一樣的算法:新生代存活率低註定要頻繁進行 Minor GC,而老年代存活率高會使 Major GC 頻率下降。

GC 類型

  • Minor GC:針對新生代 GC
  • Major GC:針對老年代 GC,一般會變成 Full GC
  • Full GC:Major GC + Minor GC

Java 收集器

枚舉根節點

OopMap 保存了類型信息,包括引用,方便 GC roots 找引用鏈

Safepoint、Safe Region

  • 到達 Safepoint,才能進行 GC
  • 常見 Safepoint
    • 方法調用
    • 循環跳轉
    • 異常跳轉
  • 如何讓全部代碼跑到 Safepoint
    • 搶斷式中斷(Preemptive Suspension):GC 發生時,強制中斷;若是代碼不在 Safepoint,讓其跑到 Safepoint,通常不採用
    • 主動式中斷(Voluntary Suspension):各個線程主動去輪訓該標誌,如 test 指令
  • Safe Region 是指代碼引用不會發生變化的區域,GC 時安全

比較

名稱 新生代 or 老年代 特色 適用場景
Serial 新生代 單線程 在客戶端簡單而高效
ParNew 新生代 Serial 多線程版本 適合於多核 CPU
Parallel Scavenge 新生代 注重於吞吐量(吞吐量 = 運行用戶代碼時間 / (運行用戶代碼時間 + 垃圾收集時間)) 注重吞吐量的場景
Serial Old 老年代 Serial 老年代版本 CMS 後備方案,配合 Parallel Scavenge
Parallel Old 老年代 ParNew 老年代版本 配合 Parallel Scavenge 使用
CMS(Concurrent Mark Sweep) 老年代 響應時間最短爲目標 響應時間比較短,吞吐量相對於 G1 大一些
G1(Garbage first) - 可預測的停頓,能夠規定停頓時間 對響應時間有要求的,吞吐量要求不高的

參數

  • UseParallelOldGC(PS + Parallel Old)
  • SurvivorRatio(Eden、Survivor 比例)
  • PretenureSizeThreShold(直接晉升到老年代大小)
  • MaxTenuringThreshold(晉升老年代年齡)
  • UseAdaptiveSizePolicy(動態調整區域大小、進入老年代年齡)
  • HandlePromotionFailure(是否容許擔保失敗)
  • ParallelGCThreads(GC 內存回收線程數)
  • GCTimeRatio(GC 佔用總時間比率,在 PS 收集器有效)
  • MaxGCPauseMillis(GC 最長停頓時間,在 PS 收集器有效)
  • CMSInitiatingOccupancyFraction(CMS,空間使用多少開始 GC)
  • UseCMSCompactAtFullCollection(CMS,是否在 CMS 完成以後整理內存碎片)
  • CMSFullGCsBeforeCompaction(CMS,在若干次垃圾收集後啓動一次碎片整理)

更多參數

CMS

STW:stop the world,中止全部線程

流程

  1. 初始標記(CMS initial mark),STW,時間很短,找出全部 GC roots 能夠關聯的對象
  2. 併發標記(CMS concurrent mark),不會 STW,時間很長,併發執行,找出全部須要清理的對象
  3. 從新標記(CMS remark),STW,時間比較短,修正併發階段可能致使標記錯誤的對象
  4. 併發清除(CMS concurrent sweep),不會 STW,清理

缺點

  • 對於單核 CPU 不友好,GC 時候佔用不少 CPU 時間,下降吞吐量
  • 產生浮動垃圾(Floating Garbage),因爲 CMS 和線程是並行執行,因此不能等到空間滿了以後才 GC,此時產生的垃圾叫作 Floating Garbage;CMS 運行期間可能因爲 Floating Garbage 致使內存佔滿,會觸發 Concurrent Mode Failure,啓動 Serial Old 收集器
  • 因爲採用 Mark Sweep 算法致使內存碎片,須要 Full GC 去整理內存

G1

算法

  • 分爲多個 Region,計算出每一個 Region 回收價值,進行回收
  • Region 保存了 Remembered Set,記錄了引用關係;在寫操做進行時增長了屏障,

流程

  1. 初始標記(Initial Marking),STW,時間很短,找出全部 GC roots 能夠關聯的對象
  2. 併發標記(Concurrent Marking),不會 STW,時間很長,併發執行,找出全部須要清理的對象
  3. 最終標記(Final Marking),STW,時間比較短,修正併發階段可能致使標記錯誤的對象,根據Remembered Set Logs修正 根據Remembered Set
  4. 篩選回收(Live Data Counting And Evacuation),STW,時間短,根據 Region 的價值回收垃圾

優勢

  • 並行與併發
  • 分代收集,內部區分年輕代和老年代,不須要配合其餘收集器
  • 空間整合,將內存分爲多個 Region,Region 之間是基於複製算法
  • 可預測的停頓,能夠指定 M 毫秒內垃圾收集時間不超過 N 毫秒

GC 日誌

[GC (Allocation Failure) [Tenured: 6144K->6593K(10240K), 0.0030056 secs] 8201K->6593K(19456K), [Metaspace: 2994K->2994K(1056768K)], 0.0030760 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [Tenured: 6593K->6577K(10240K), 0.0034362 secs] 6593K->6577K(19456K), [Metaspace: 2994K->2994K(1056768K)], 0.0034777 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
  • GC 類型 + 老年代回收狀況 + 總內存回收狀況 + Metaspace 回收狀況 + 時間(用戶態 + 內核態 + 牆鍾時間)
  • 不一樣的收集器會用不一樣的字段,基本上保持一致

內存分配、回收

  • 對象優先在 Eden 分配
  • 大對象直接進入老年代(儘可能不要有短命的大對象,Major GC 頻繁)
  • 長期存活的對象進入老年代
  • 動態對象年齡判斷(若是相同年齡的對象大小佔用 Survivor(from) 空間的 1/2,直接進入老年代)

空間擔保分配

  • 若是老年代剩餘空間比年輕代佔用空間大(說明裝得下全部的年輕代,沒有風險),Minor GC 安全
  • 不然,若是擔保失敗且老年代剩餘空間比歷次晉升到老年代的對象大小總和大,嘗試進行 Minor GC
  • 不然,進行 Full GC
  • 若是嘗試進行的 Minor GC 失敗,進行一次 Full GC

JVM 工具

命令行

命令行少用,費時。

  • jps: 顯示虛擬機進程
  • jstat: 虛擬機運行數據
  • jinfo: 虛擬機配置信息
  • jmap: 內存轉儲快照(heapdump)
  • jhat:分析 heapdump 文件,不建議使用,功能簡陋,佔用資源
  • jstack:顯示虛擬機進程快照

GUI

  • jconsole 老版本
  • jvisualvm 新版本,推薦使用,插件化,功能更多,好比 btrace(擴展代碼

JVM 調優案例

  • 高性能硬件上的程序部署策略:每一個 JVM 分配大內存,GC 時間長,STW 時間過長,影響用戶體驗;須要分紅不少 JVM,每一個 JVM 佔小內存。
  • 堆外內存致使溢出:OOM 緣由是 null;Direct Memory 只有在 Full GC 纔會回收
  • 外部命令致使系統緩慢:調用 shell 腳本致使系統緩慢;刪除 shell 腳本,改爲 java 命令
  • JVM 進程崩潰,線程過多:異步方式調用接口等待返回;改用生產者消費者模式

Java 內存模型

線程數據存儲

  • 全部變量都存在主內存(Main Memory)
  • 每一個線程都有本身的工做內存(Main Memory)

volatile

  • 可見性:修改以後的結果會當即刷新到主內存中,並不表明併發安全(如 i++),儘可能使用原子性的操做來操做 volatile 數據
  • 有序性:禁止編譯器重排序優化
  • 輕量

原則

  • 原子性、可見性、有序性
  • 先行發生關係(happens-before)
    • 程序次序原則:在一個線程內,按照代碼順序執行
    • 管程鎖定原則:unlock 必定發生在 lock 以前
    • volatile 變量規則:寫優先於讀
    • 線程啓動規則:start 優先
    • 線程終止規則:全部操做先行於對此線程的終止
    • 線程中斷規則:interrupt() 優先於檢測到中斷事件
    • 對象終結原則:初始化優先於 finalize()
    • 傳遞性:A 比 B 先,B 比 C 先,A 必定比 C 線

線程實現(操做系統方面)

  • 輕量級進程,即線程
  • 調度方式:搶佔式

線程狀態

  • New:剛剛建立
  • Runnable:正在運行
  • Waiting:等待,如 Object.wait()、Thread.join()
  • Timed Waiting:限期等待,在必定時間喚醒,設置了 timeout、Thread.sleep() 方法
  • Blocked,阻塞,如 synchronized
  • Terminated:終止

線程安全

  • 不可變對象:共享數據爲 final,類爲 final,要想修改陳勝新對象
  • 絕對線程安全:絕對安全,不可能
  • 相對線程安全:普通的 Java 類所處的水平
  • 線程兼容:使用同步手段保證線程安全
  • 線程對立:怎麼都線程不安全,應該避免

線程安全實現

  • 阻塞
    • synchronized 同步
    • RentrantLock 可重入鎖(等待可中斷、能夠實現公平鎖、能夠綁定多個解鎖條件)
  • 非阻塞(樂觀鎖)
    • CAS

鎖優化

  • 適應性自旋鎖(Adaptive Spining):進入 synchronized 以前,若是對象的鎖是輕量級鎖,會忙循環一段時間(自適應),這麼作是由於在實踐中,持有鎖的線程會很快釋放鎖,減小切換上下文開銷
  • 鎖消除(Lock Elimination):一些代碼上須要同步,可是沒有共享數據的,消除鎖
  • 鎖粗化(Lock Coarsening):對同一個對象反覆加鎖、解鎖,會把加鎖操做範圍擴大,例如原來在循環裏,後放在循壞外
  • 輕量鎖(Lightweight Locking):當有兩個線程競爭的時候使用,第一個線程經過 CAS 得到鎖,若是成功,執行代碼;第二個線程經過 CAS 未得到鎖,會自旋,若是自旋以後再次得到鎖仍是失敗,膨脹爲重量級鎖
  • 偏向鎖(Baised Locking):只有一個線程時使用,只須要 CAS 部分字段(線程 ID),若是成功,得到鎖,以後只須要比較線程 ID 便可;若是失敗,說明不是偏向該線程,檢查存在線程是否活着,若是存活,撤回偏向鎖,膨脹爲輕量級鎖;若是死去,撤回偏向鎖,偏向當前線程。線程衝突較多建議禁用偏向鎖,-XX:-UseBiasedLocking
相關文章
相關標籤/搜索