鍍金池/ 教程/ Android/ 深入理解Java虛擬機(jī)
Launch mode 和 Intent flags專題
Canvas & Drawables
UTAustinX_UT.9.01x: Effective Thinking Through Mathematics
《JavaScript 語言精粹》
Memory leak專題
React基礎(chǔ)
《Test Driven Development: By Example》一書
Developer tools
安卓開發(fā)技能樹
<a rel="nofollow" href="https://mp.weixin.qq.com/s?__biz=MzA3NDM
Best Practices for Interaction and Engagement
各個(gè)安卓版本引入的主要新特性
Building Apps with Connectivity &amp; the Cloud
List.toArray()再強(qiáng)轉(zhuǎn)是一定會失敗的
深入Android frameworks
Google dev 100 days系列視頻
Building Apps with Contacts &amp; Sign-In
關(guān)系型數(shù)據(jù)庫設(shè)計(jì)范式
《App研發(fā)錄》一書
REST API設(shè)計(jì)
Google IO 2015摘要
自定義View/ViewGroup以及高性能實(shí)現(xiàn)自定義UI
安卓系統(tǒng)點(diǎn)擊事件處理
《50 Android Hacks》一書
Building Apps with Content Sharing
Flux基礎(chǔ)
<a rel="nofollow" href="http://developer.android.com/training/in
依賴注入(以Dagger 2為例)
Java同步機(jī)制
Java對象內(nèi)存的使用情況
JSR133(Java memory model)
Google官方Material Design手冊(<a rel="nofollow" href="http://develop
Futurice公司安卓團(tuán)隊(duì)的建議
安卓性能優(yōu)化
  • 1.
Best Practices for Performance
<a rel="nofollow" href="http://www.vogella.com/tutorials/Android
<a rel="nofollow" href="http://blog.danlew.net/2014/11/19/styles
Handling Runtime Changes
<a rel="nofollow" href="http://www.vogella.com/tutorials/Android
Building Apps with Graphics &amp; Animation
<a rel="nofollow" href="http://tools.android.com/tech-docs/new-b
Android項(xiàng)目架構(gòu)
MVP(Model-View-Presenter)模式
<a rel="nofollow" href="http://www.infoq.com/cn/es6-in-depth/"">
《Android源碼設(shè)計(jì)模式解析與實(shí)戰(zhàn)》一書
Rx在Android中的最佳實(shí)踐
函數(shù)調(diào)用時(shí),傳遞參數(shù)應(yīng)該是不可變的(Immutable)
ProGuard
面向?qū)ο罅笤瓌t(SOLID+)
深入理解Java虛擬機(jī)
深入Java深淺拷貝、immutable、unmodifiable
Best Practices for User Input
UI上的一些高效方式/最佳實(shí)踐
<a rel="nofollow" href="https://blog.stylingandroid.com/ripples-
Best Practices for User Interface
安卓測試驅(qū)動開發(fā)/安卓測試驗(yàn)證
暗時(shí)間:學(xué)會正確思考
技術(shù)筆記
Aspect Oriented Programming(AOP)
Best Practices for Background Jobs
安卓系統(tǒng)動效專題
Feed系統(tǒng)的設(shè)計(jì)
Data binding(MVVM,Model-View-ViewModel)
Effective Java一書筆記
<a rel="nofollow" href="http://developer.android.com/training/in
Rx (Reactive eXtention)
MultiDex專題
一些很棒的點(diǎn)子
WebRTC

深入理解Java虛擬機(jī)

第二部分 自動內(nèi)存管理機(jī)制

第二章 Java內(nèi)存區(qū)域與內(nèi)存溢出異常

  • JVM內(nèi)存區(qū)域
    http://wiki.jikexueyuan.com/project/notes/images/jvm_memory_area.png" alt="jvm_memory_area.png" />
    • 程序計(jì)數(shù)器:類似x86 EIP,每個(gè)線程都有一個(gè)程序計(jì)數(shù)器;執(zhí)行native代碼時(shí)計(jì)數(shù)器值為空;唯一不會拋出OOM的區(qū)域;
    • Java虛擬機(jī)棧:線程私有;即函數(shù)調(diào)用棧,保存函數(shù)局部變量;
    • Native方法棧:執(zhí)行Native代碼的函數(shù)調(diào)用棧;
    • Java堆:新創(chuàng)建的對象存放區(qū)域;
    • 方法區(qū):保存已加載的類信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù);有以永久代實(shí)現(xiàn)此區(qū)域的,也有以獨(dú)立GC實(shí)現(xiàn)的;
    • 運(yùn)行時(shí)常量池:是方法區(qū)的一部分;String.intern()方法有可能對此區(qū)域造成大的影響(依賴于JVM實(shí)現(xiàn));
    • 直接內(nèi)存:NIO模塊,為了提高內(nèi)存訪問效率,直接分配堆外內(nèi)存,直接操作;
  • HotSpot虛擬機(jī)對象創(chuàng)建
    首先檢查類的符號引用是否在常量池中,并檢查該類是否已加載、解析、初始化;如果沒有,則先進(jìn)行加載;
    然后為對象分配內(nèi)存空間,對象所占空間在類加載完成后即可確定;
    堆內(nèi)存管理大致有兩種方式:指針碰撞;空閑列表。依賴于GC時(shí)是否會對回收的內(nèi)存進(jìn)行壓縮整理;
    分配內(nèi)存的同步性保證:分配過程原子化;將內(nèi)存分配按照線程劃分在不同的空間中(本地線程分配緩沖,TLAB)。后者可通過JVM參數(shù)控制;
    內(nèi)存分配完后,將內(nèi)存空間初始化為零值,不包括“對象頭”,初始化工作可以提前至TLAB分配時(shí)進(jìn)行;
    設(shè)置“對象頭”,例如:對象是哪個(gè)類的實(shí)例,類的元數(shù)據(jù)信息地址,對象hash值,GC分代年齡等信息;根據(jù)虛擬機(jī)當(dāng)前的運(yùn)行狀態(tài)不同,是否啟用偏向鎖等,對象頭會有不同設(shè)置方式;
    執(zhí)行類的<init>方法;
  • HotSpot虛擬機(jī)對象的內(nèi)存布局
    分為三部分:Header,實(shí)例數(shù)據(jù),對其填充;
    實(shí)例數(shù)據(jù)部分,相同寬度的字段會分配到一起;滿足該前提下,父類的變量出現(xiàn)在子類前;CompactFields參數(shù)為true時(shí),子類中較窄的變量可能會插入到父類變量的空隙之中;
    對象大小8字節(jié)對其;
  • 對象的訪問定位
    在堆上建立了對象,使用的時(shí)候基本都是通過棧上的reference來操作堆上的對象;
    目前主流有兩種訪問方式:使用句柄;直接指針;
    • 使用句柄
      在堆上劃分一塊內(nèi)存作為句柄池,reference指向的是對象的句柄地址,句柄中包含了對象實(shí)例數(shù)據(jù)與類型數(shù)據(jù)各自的具體地址信息;句柄地址是穩(wěn)定的,對象移動時(shí)只需修改句柄的內(nèi)容,不必修改reference;
    • 直接指針
      reference直接指向堆上對象地址;訪問速度快,只需一次指針定位;HotSpot使用直接指針方式;
  • 各種可能的OutOfMemoryError異常
    • 堆溢出
      java.lang.OutOfMemoryError: Java heap space...

      log中明確指出heap space,可通過JVM參數(shù)設(shè)置堆大小、最大大小、發(fā)生OOM后dump堆上數(shù)據(jù);
      一般通過分析dump數(shù)據(jù),確認(rèn)內(nèi)存中的對象是否必要,即確定是內(nèi)存泄漏還是內(nèi)存溢出;
      如果是內(nèi)存泄漏,可進(jìn)一步查看泄漏對象到GC Roots的引用鏈,根據(jù)引用鏈,基本就能定位泄漏代碼的位置了;
      如果是內(nèi)存溢出,則可通過增加JVM堆內(nèi)存、審查代碼,減少對象生命周期,減少程序運(yùn)行期間的內(nèi)存消耗;

    • 虛擬機(jī)棧與本地方法棧溢出
      StackOverFlowError,OutOfMemoryError
      當(dāng)每個(gè)線程分配的棧容量越大時(shí),發(fā)生StackOverFlowError時(shí)創(chuàng)建的線程數(shù)越少,因?yàn)槊總€(gè)進(jìn)程的內(nèi)存是有限的,棧容量越大,則允許的線程數(shù)越少;通過減少線程數(shù)、更換64位虛擬機(jī)(增加內(nèi)存)、減少最大堆和棧容量,均可緩解這一錯誤;
    • 方法區(qū)與運(yùn)行時(shí)常量池溢出
      java.lang.OutOfMemoryError: PermGen space...

      永久代區(qū)域內(nèi)存大小可以配置,如果方法區(qū)以永久代實(shí)現(xiàn),則String.intern()方法就可能導(dǎo)致該錯誤;
      類的卸載條件非??量?,如果運(yùn)行時(shí)使用了大量的動態(tài)生成類,有可能導(dǎo)致該錯誤;

    • 直接內(nèi)存溢出
      java.lang.OutOfMemoryError: ...

      在dump文件中看不出明顯異常;
      直接內(nèi)存區(qū)域可以通過JVM參數(shù)配置,默認(rèn)與堆最大值一樣;

第三章 垃圾收集器與內(nèi)存分配策略

  • 是否可被回收
    • 引用計(jì)數(shù)法
      為每個(gè)對象添加一個(gè)引用計(jì)數(shù)器,當(dāng)計(jì)數(shù)器為0時(shí),可被回收;Python等語言使用,Java不是;無法解決循環(huán)引用的問題;
    • 可達(dá)性分析
      從GCRoots出發(fā),對所有的對象引用關(guān)系圖進(jìn)行一次遍歷,不可達(dá)的對象可被回收;Java使用;GCRoots包括:虛擬機(jī)棧中引用的對象;方法區(qū)中靜態(tài)類屬性引用的對象;方法區(qū)中常量引用的對象;本地方法棧中JNI引用的對象;
    • 四種引用類型
      • 強(qiáng)引用
        普通賦值即為強(qiáng)引用;只要有強(qiáng)引用,就不會被GC;
      • 軟引用(SoftReference)
        用來引用那些有用,但非必須的對象;在系統(tǒng)將要發(fā)生內(nèi)存溢出錯誤之前,會將軟引用指向的對象列入回收范圍并進(jìn)行二次回收,如果仍內(nèi)存不足,將拋出OOM;
      • 弱引用(WeakReference)
        作用與軟引用類似,但強(qiáng)度更弱;GC過程中,無論內(nèi)存是否足夠,只被弱引用指向的對象,都將被回收;
      • 虛引用(PhantonReference)
        對對象的生存周期完全沒有影響,也無法通過虛引用來獲取對象實(shí)例,僅僅能在對象被回收時(shí),得到一個(gè)系統(tǒng)通知(只能通過是否被加入到ReferenceQueue來判斷是否被GC,這也是唯一判斷對象是否被GC的途徑);
    • finalize()方法
      當(dāng)可達(dá)性分析結(jié)果為不可達(dá)時(shí),GC器首先會判斷是否需要執(zhí)行finalize()方法,只有當(dāng)類重寫了finalize方法,且該對象的finalize方法未被調(diào)用,才需要執(zhí)行;
      finalize方法被放在一個(gè)虛擬機(jī)自動建立、低優(yōu)先級的線程調(diào)用,且不保證執(zhí)行完畢;
      不需要執(zhí)行finalize時(shí),將被GC;
      在finalize方法中,對象可以通過把自己掛到GCRoots引用鏈上進(jìn)行GC逃逸,但只有一次機(jī)會;
      而如果使用虛引用,則可以避免GC逃逸的問題出現(xiàn),這也是虛引用的第二個(gè)使用場景;
      千萬不要把finalize()方法和C++的析構(gòu)函數(shù)等同;
    • 回收方法區(qū)
      包括常量、類,這兩部分;
      常量與普通對象類似,判斷引用狀態(tài)即可;
      類的判斷比較苛刻:該類的所有對象均已被回收;加載該類的ClassLoader已被回收;該類對應(yīng)的java.lang.Class對象未在任何地方被引用。而且這只是充分條件;
      在使用反射、動態(tài)代理、CGLib等ByteCode框架、動態(tài)生成JSP以及OSGI等頻繁自定義ClassLoader的場景都需要虛擬機(jī)具備類卸載的功能,以避免方法區(qū)溢出;
  • 垃圾收集算法
    • 標(biāo)記-清除算法
      第一階段,判定對象可回收之后,將其標(biāo)記,第二階段,統(tǒng)一回收被標(biāo)記的對象;
      兩個(gè)階段效率都不高;另外存在碎片化問題;
    • 復(fù)制算法
      將可用內(nèi)存均分為兩塊,每次只使用其中一塊,當(dāng)一塊用完之后觸發(fā)GC,將存活的對象復(fù)制到另一塊,再一次性清理已使用的一塊;
      效率高,不存在內(nèi)存碎片;但是內(nèi)存縮小一半,空間開銷大;
      現(xiàn)代商用JVM均使用復(fù)制算法來回收新生代,HotSpot虛擬機(jī)默認(rèn)將內(nèi)存分為8:1:1三塊,一塊Eden空間,兩塊較小的Survivor空間,每次使用Eden和一塊Survivor,用完后將存活對象復(fù)制到另一塊Survivor空間,集中回收已用部分;當(dāng)一塊Survivor不夠復(fù)制時(shí),將通過分配機(jī)制進(jìn)入老年代;
    • 標(biāo)記-整理算法
      標(biāo)記過程同“標(biāo)記清除算法”,標(biāo)記結(jié)束后,通過將所有存活對象都向一端移動,然后直接清理邊界外的內(nèi)存;
    • 分代收集算法
      現(xiàn)代商用JVM均采用分代收集算法;一般把堆分為新生代和老年代;
      新生代一般98%的對象都會很快被回收,使用復(fù)制算法;
      老年代使用標(biāo)記-清理,或者標(biāo)記-整理算法;
  • HotSpot的算法實(shí)現(xiàn)
    ...

第七章 虛擬機(jī)類加載機(jī)制

  • 類的生命周期
    • 加載、驗(yàn)證、準(zhǔn)備、解析、初始化、使用、卸載
    • 驗(yàn)證、準(zhǔn)備、解析,通常稱為鏈接過程
    • 加載、驗(yàn)證、準(zhǔn)備、初始化、卸載,這五個(gè)階段的開始順序是確定的,解析階段可能在初始化之后再開始,用于支持動態(tài)綁定;僅僅是開始順序,并不是進(jìn)行/完成的順序;
    • 類加載的時(shí)機(jī)并未明確規(guī)定,但是初始化明確規(guī)定,當(dāng)且僅當(dāng)以下5中情況,若類未初始化,必須立即對類進(jìn)行初始化:
      • 遇到new、getstatic、putstatic或invokestatic這4條字節(jié)碼指令時(shí)(new一個(gè)類,訪問static成員/方法,static final成員除外,它們在編譯期放入了常量池);
      • 使用java.lang.reflect包得方法對類進(jìn)行反射調(diào)用的時(shí)候;
      • 初始化一個(gè)類,然而其父類未初始化時(shí),先初始化父類;
      • 虛擬機(jī)啟動時(shí)用戶指定的要執(zhí)行的主類;
      • 當(dāng)使用JDK 1.7的動態(tài)語言支持時(shí),如果一個(gè)java.lang.invoke.MethodHandle實(shí)例最后的解析結(jié)果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且方法句柄對應(yīng)的類未初始化;
    • 這5種行為稱為主動引用,可能會觸發(fā)類的初始化;其他的行為均不會觸發(fā)類的初始化,稱為被動引用,例如:
      • 通過子類引用父類的靜態(tài)成員,不會導(dǎo)致子類初始化
      • 通過數(shù)組定義(new一個(gè)數(shù)組),并不會觸發(fā)數(shù)組元素類的初始化,但是虛擬機(jī)會自動生成一個(gè)直接繼承于Object的子類,該類封裝了對數(shù)組的訪問
      • static final成員會被放入常量池,訪問它們不會觸發(fā)類的初始化
    • 接口的初始化觸發(fā)條件與類基本一致,僅有第三點(diǎn)不同:一個(gè)接口初始化時(shí),不要求父接口完成初始化,只有在真正使用父接口時(shí)才會觸發(fā)父接口的初始化
  • 類加載的過程

    • 加載
      • 通過類的全限定名,獲取定義此類的二進(jìn)制字節(jié)流
      • 將字節(jié)流中的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)
      • 在內(nèi)存中生成一個(gè)代表這個(gè)類的java.lang.Class對象,作為方法區(qū)這個(gè)類的各種數(shù)據(jù)的訪問入口
      • 第一階段最靈活,二進(jìn)制流可以從任何位置、以任何方式獲取,衍生了許多強(qiáng)大的技術(shù)(zip包、網(wǎng)絡(luò)、動態(tài)代理),通過自定義類加載器即可利用這一靈活性
      • 數(shù)組的類型是虛擬機(jī)自動創(chuàng)建的,但也和數(shù)組元素的類加載器緊密相關(guān)
    • 驗(yàn)證
      • 編譯器會對代碼進(jìn)行檢查
      • 虛擬機(jī)會對字節(jié)碼進(jìn)行檢查,包括:文件格式驗(yàn)證、元數(shù)據(jù)驗(yàn)證(修飾符、可見性、繼承關(guān)系)、字節(jié)碼驗(yàn)證(數(shù)據(jù)流、控制流分析,確定語義合法、符合邏輯,StackMapTable)、符號引用驗(yàn)證(可查找、可訪問等)
    • 準(zhǔn)備
      • 為static變量分配內(nèi)存,設(shè)置零值(而非代碼中定義的初始值,初始值在自動生成的()函數(shù)中進(jìn)行);為static final變量直接設(shè)置初始值
    • 解析
      • 符號引用轉(zhuǎn)換為直接引用,有些只有在運(yùn)行時(shí)才能完成解析
    • 初始化
      • ()函數(shù),將static代碼塊、static(無final修飾)變量的賦值組合起來而成;static代碼塊訪問static變量時(shí),變量必須已經(jīng)定義(代碼位置相關(guān)性,可寫不可讀)
      • 父類的()函數(shù)一定先于子類的執(zhí)行,因此父類的static代碼塊先于子類的static變量賦值操作
      • ()函數(shù)對于接口來說非必須,()函數(shù)的線程安全性由虛擬機(jī)保證,static代碼塊中要避免耗時(shí)操作
    • 類加載器

      • 加載一個(gè)類到內(nèi)存中
      • 比較兩個(gè)類是否相等(equals()、isAssignableFrom()、inInstance()、instanceof操作),只有在兩個(gè)類是由同一個(gè)類加載器加載的前提下才有意義
      • 雙親委派模型(Parents Delegation Model)
        BootstrapClassLoader、sum.misc.Launcher$ExtClassLoader、sun.misc.Launcher$App-ClassLoader
        http://wiki.jikexueyuan.com/project/notes/images/java_class_loader_relation.jpeg" alt="java_class_loader_relation.jpeg" />
        除了BootstrapClassLoader,其他所有的類加載器都應(yīng)有父類加載器,而且父子關(guān)系不是以繼承方式實(shí)現(xiàn),而是以組合方式:一個(gè)類加載器收到類加載請求后,首先調(diào)用父類加載器的加載方法,每一層次均如此,如果父類加載器無法完成加載,子加載器才嘗試自己加載。
        保證Java類體系的正確性。自定義ClassLoader一般把加載邏輯放在findClass中。

        protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
          Class<?> clazz = findLoadedClass(className);
        
          if (clazz == null) {
              ClassNotFoundException suppressed = null;
              try {
                  clazz = parent.loadClass(className, false);
              } catch (ClassNotFoundException e) {
                  suppressed = e;
              }
        
              if (clazz == null) {
                  try {
                      clazz = findClass(className);
                  } catch (ClassNotFoundException e) {
                      e.addSuppressed(suppressed);
                      throw e;
                  }
              }
          }
        
          return clazz;
        }    
      • 非雙親委派模型:JNDI、JDBC、JCE、JAXB、JBI;程序動態(tài)性,OSGI,代碼熱替換、模塊熱部署等;

第八章 虛擬機(jī)字節(jié)碼執(zhí)行引擎

  • 基于棧的執(zhí)行引擎,而非基于寄存器
  • 即時(shí)編譯本地代碼執(zhí)行,JIT
  • 局部變量表
    • slot
    • slot重用對垃圾回收可能會有影響,但是通過JIT可以解決
    • 局部變量不會被自動賦予零值,但編譯器有些情況下會給提示
  • 操作數(shù)棧
    • 不同棧幀之間可能有重疊,避免數(shù)據(jù)復(fù)制開銷
    • 動態(tài)鏈接,每個(gè)棧幀中都包含一個(gè)指向運(yùn)行時(shí)常量池中該棧幀所屬方法的引用,用于支持動態(tài)鏈接
    • 方法返回地址
    • 附加信息:調(diào)試信息等
  • 方法調(diào)用
    • 解析
      • invokestatic:靜態(tài)方法
      • invokespecial:構(gòu)造函數(shù)(())、私有方法、父類方法
      • invokevirtual:調(diào)用虛方法
      • invokeinterface:調(diào)用接口方法
      • invokedynamic:動態(tài)解析
    • 分派
      • 靜態(tài)分派(Method Overload Resolution):方法重載,編譯期分派,編譯器選擇最合適的重載版本;類似于C++的編譯時(shí)多態(tài):函數(shù)重載,模板;
      • 動態(tài)分派(Dynamic Dispatch):方法重寫,運(yùn)行期分派;類似于C++的運(yùn)行時(shí)多態(tài):虛函數(shù);
    • 動態(tài)類型語言支持
      ...