簡介JVM
簡介JVM
1.回收的是什么? 對象
1.1 什么是對象
? Java 萬物皆對象
1.2對象里有什么?
1.2.1 從代碼上看
? 屬性:表示有什么
方法 : 表示能干什么
1.2.2 從jvm底層看
? 對象頭
MarkWord:存儲 :對象哈希碼、GC 分代年、鎖狀態標志、線程持有鎖、偏向線程ID、偏向時間戳等
synchronized 用的鎖就是存在 Mark Word 中,在運行期間,Mark Word 里存儲的數據會隨著鎖標志位的變化而變化,會有以下五種變化。注意無鎖態和偏向鎖的鎖標志位相同,額外增加了一個字節來判斷。在 32 位的 HotSpot 虛擬機 中對象未被鎖定的狀態下,Mark Word 的32個Bits空間中的25Bits用于存儲對象哈希碼(HashCode),4Bits 用于存儲對象分代年齡,2Bits 用于存儲鎖標志位,1Bit 固定為 0,表示非偏向鎖。
? Klass (數組對象的話還有一個length):用來確定該對象是哪個類的實例。
對象實際數據:存儲對象的各種類型的字段內容(包括從父類繼承的)
對齊填充:對齊數據不是必然存在的,只起占位符的作用,沒有特別的含義
訪問方式:
? 句柄
在Java 堆中將會劃分出一塊內存作為【句柄池】,Java棧中reference中存儲的就是對應的句柄地址
句柄中包含了 :對象實例數據 和 類型數據各自的具體地址信息
直接指針
reference中存儲的就是對象的地址
比較
直接指針最大的好處時:速度快,節省額一次指針定位的時間開銷
句柄: reference中存儲的時穩定句柄地址,在對象被移動(垃圾收集時移動對象是很普遍的行為)時只會改變句柄中的實例數據指針,reference不會改變
Hot Spot 使用的時直接指針方式
2.對象生命周期
2.1 存放在哪?
? 對象的實例(instantOopDesc)保存在堆上,對象的元數據(instantKlass)保存在方法區,對象的引用保存在棧上
2.2 生命周期
2.2.1 加載
? 1.通過一個類的全限定名來獲取定義此類的二進制字節流
2.將這個字節流所代表的的靜態存儲結構轉化為方法區的運行時數據結構
3.在內存中生成一個代表這個類的java.lang.Class對象,作為方法區這個類的各種數據的訪問入口
2.2.2 驗證
目的:為了保證class文件的字節流中包含的信息符合當前虛擬機的要求,并且不會危害虛擬機自身的安全
操作內容:
- 文件格式驗證: 驗證字節流是否符合Class文件格式的規范,并且能被當前版本的虛擬機處理。
- 元數據驗證:對字節碼描述的信息鏡像語義分析,以保證其描述的信息符合Java語言規范的要求。
- 字節碼驗證:(最復雜的一個階段)通過數據流和控制流分析,確定程序語義是合法的。符合邏輯的。
- 符號引用驗證:發生在虛擬機將符號引用轉化為直接引用的時候,這個轉化動作將在連接的第三個階段-----解析階段發生。符號引用驗證可以看做對類自身以外(常量池中各種符號引用)的信息進行匹配性校驗,確保解析動作能正常執行。
2.2.3 準備
正式為類變量分配內存并設置類變量初始值的階段,這些變量所使用的的內存都將在方法區中進行分配。
- 類變量指的是被static修飾的變量
- 類變量不包含實例變量,實例變量將會在對象實例化的時隨著對象一起分配在Java堆中,令這里的初始值對于基本類型一般是0或false,引用類型是null。
2.2.4 解析
解析階段是虛擬機將常量池內的符號引用替換為直接引用的過程
-
符號引用: 用以一組符號來描述所引用的目標,符號可以是任何形式的字面量,只要使用是能無歧義地定位到目標即可。符號引用于虛擬機實現的內存布局無關,引用的目標并不一定已經加載到內存中
-
直接引用:可以是直接指向目標的指針、相對偏移量或是一個能間接定位到目標的句柄。直接引用于虛擬機實現的內存布局有關,同一個符號引用在不同虛擬機實例翻譯出來的直接引用一般不會相同。如果有了直接引用,那么引用的目標必定已經在內存中存在。
-
解析動作主要針對
-
類或接口解析
-
字段解析
-
類方法解析
-
接口方法解析
-
方法類型
-
方法句柄
-
調用點限定符
-
2.2.5 初始化
-
類加載過程的最后一步,真正開始執行類中定義的Java程序代碼(或者說字節碼)
-
在準備階段,變量已經賦過一次系統要求的初始值,而在初始化階段,則根據程序員通過程序制定的主觀計劃去初始化類變量和其他資源,或者而已從另一個角度來表達:初始化階段是執行類構造器< clinit >()方法的過程:
1)< clinit >()方法是有編譯器自動收集類中的所有類變量的賦值動作和靜態語句塊(static{})中的語句合并產生的,編輯器收集的順序是由語句在源文件中出現的順序決定的,靜態語句塊中只能訪問到定義在靜態語句之前的變量,定義在它之后的變量,在前面的靜態語句塊可以賦值,但是不能訪問。
2)< clinit >()方法與類的構造函數不同,它不需要顯示地調用父類構造函數,虛擬機會保證在子類的< clinit >()方法執行之前,父類的< clinit >()方法已經執行完畢。因此在虛擬機中第一個被執行的< clinit >()方法的類肯定是java.lang.Object。
3) 由于父類的< clinit >()方法先執行,也就意味著弗雷中定義的靜態語句塊要優先于子類的變量賦值操作。
4) < clinit >()方法對于類或接口來說并不是必須的,如果一個來中沒有靜態語句塊,也沒有對變量的賦值操作,那么編譯器可以不為這個類生成< clinit >()方法。
5) 接口中不能使用靜態語句塊,但仍然有變量初始化的賦值操作,因此接口也有< clinit >()方法,但接口與類不同的是:執行接口< clinit >()方法時不需要父接口的< clinit >()方法。只有當父接口中定義的變量使用時,父接口才會初始化。
6) 虛擬機會保證一個類的< clinit >()方法在多線程環境中被正確地加鎖、同步,如果多個線程同時去初始化一個類,那么只會有一個線程去執行這個類的< clinit >()方法,其他線程都是阻塞等待,直到活動線程執行< clinit >()方法完畢(其他線程進入后不會再次執行< clinit >()方法,同一個類加載器下,一個類型只會初始化一次)。
還有剩下的2個過程 使用以及卸載
2.3 類加載器
- 對于任意一個類,都需要有加載它的類加載器和這個類本身一同確立其在Java虛擬機中的唯一性,每一個類加載器,都擁有一個獨立的類名稱空間
- 從Java虛擬機的角度來講,只存在兩種不同的類加載器:
- 一種是啟動類加載器(Bootstrap ClassLoader),這個類加載器是使用C++語言實現,是虛擬機自身一部分;
- 另一種就是所有其他類的類加載器,這些都是有Java語言實現,獨立于虛擬機外部,并且全部繼承抽象類java.lang.ClassLoader。
從Java開發人員角度來看:
-
啟動類加載器(Bootstrap ClassLoader)
-
擴展類加載器(Extension ClassLoader)
-
應用程序類加載器(Application ClassLoader)
-
有必要,還可以加入自己定義的類加載器
類加載器雙親委派模型
- 要求除了頂層的啟動類加載器外,其余的類加載器都應當有自己的父類加載器。這里類加載器之間的父子關系一般不會以繼承的關系來實現,而都是使用組合關系來復用父加載器的代碼
- 雙親委派模型并不是一個強制的約束模型,而是Java設計者推薦給開發者的類加載器實現,在Java中大部分類加載器都遵循這個模型,但也有例外:
- 雙親委派模型是在JDK1.2之后才被引入,為了向前兼容。在java.lang.ClassLoader添加了一個新的protected方法findClass(),而這個方法的唯一邏輯就是調用自己的loadClass()
- 由于模型的自身缺陷導致,雙親委派模型很好的解決了各個類加載器的基礎類的統一問題(越基礎的類越有上層的加載器進行加載),基礎類之所以被稱為“基類”,是因為它們總是作為被用戶代碼調用的API,但當基礎類調用用戶的代碼,則會出現失敗的問題,所以引入線程上下文類加載器(Thread Context ClassLoader),這個類可以通過java.lang.Thread類的setContextClassLoaser()方法進行設置。此類情況多用在JNDI、JDBC、JCE等
- 由于用戶對程序動態性的追求(如代碼熱替換,模塊熱部署等)。
3.JVM 組成
3.1 運行時數據區域
運行時數據區從線程的角度來說,可以分為線程私有、線程共享兩大部分:
-
線程共享
-
堆(GC 堆)
- 新生代
- ****Eden空間
- From Survivor 空間
- To Survivor 空間
- 老年代
物理上不連續,但邏輯上是連續的、存放對象的實例、其他參數 、可能拋出的異常: OOMError
- 新生代
-
方法區
- 存放被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等
- 運行時常量池
- 存放編譯期生成的各種字面量和符號引用,在類加載后進入方法區的運行時常量池
-
-
線程私有
-
程序計數器
- 一塊較小的內存空間 可以看作當前線程所執行的字節碼的行號指示器
- 如果執行的是一個Native方法,則線程的這個計數器值是空
- 此區域是唯一一個Java虛擬機規范中沒有任務OOM Error情況的區域
-
虛擬機棧
-
與線程的生命周期一致
-
每個方法在執行的同時會創建一個棧幀(動態連接、方法返回地址、一些額外的附加信息 稱為 棧幀信息)
-
局部變量表
- 存放方法內定義的局部變量和方法的參數
- 容量是以slot【變量槽】為最小單位
-
操作數棧
- 后入先出的棧
- 在方法執行的過程中,或有各種字節碼指令往操作數棧中寫入或提取,即入棧和出棧
-
動態連接
- 每一個棧幀都持有一個指向運行時常量池中該棧幀所屬方法的引用,持有這個引用是為了支持方法調用過程中【動態鏈接】
-
方法返回地址
-
一些額外的附加信息
-
-
為了進行內存的共享,有可能多個棧幀之間會有 【重合區域 】
-
一個線程的方法調用鏈可能很長【導致OOM異常的原因 】,只有棧頂的棧幀才是有效的棧幀,與之關聯的方法是當前方法
-
-
本地方法棧
- 與虛擬機棧類似,不過是與Native方法服務
- 可能拋出的異常: StackOverflowError OOMError
-
3.2 執行引擎
3.3 本地庫接口
3.4 本地方法庫
4.垃圾回收標準
4.1 對象已死? 如何確認
JVM 關于對象是否已死,有一些方法論:
-
引入計數法算法
- 為每個對象添加引用計數器,每當有一個引用該對象的時候,計數器加1
- 弊端: 當對象之間相互引用時,導致無法被回收
-
可達性分析算法
-
通過一系列稱為“GC Roots”的對象作為起始點,當對象沒有通往 GC Roots的引用鏈時,表示對象不可用
-
GCRoots的對象包括下面幾種:
(1). 虛擬機棧(棧幀中的局部變量區,也叫做局部變量表)中引用的對象。
(2). 方法區中的類靜態屬性引用的對象。
(3). 方法區中常量引用的對象。
(4). 本地方法棧中JNI(Native方法)引用的對象。
-
4.2 對象的引用狀態
- 強引用
- 代碼中普遍存在的類似"Object obj = new Object()"這類的引用,只要強引用還存在,垃圾收集器永遠不會回收掉被引用的對象。
- 軟引用
- 描述有些還有用但并非必需的對象。在系統將要發生內存溢出異常之前,將會把這些對象列進回收范圍進行二次回收
- Java中的類SoftReference表示軟引用
- 弱引用
- 描述非必需對象。被弱引用關聯的對象只能生存到下一次垃圾回收之前,垃圾收集器工作之后,無論當前內存是否足夠,都會回收掉只被弱引用關聯的對象
- Java中的類WeakReference表示弱引用
- 虛引用
- 這個引用存在的唯一目的就是在這個對象被收集器回收時收到一個系統通知,被虛引用關聯的對象,和其生存時間完全沒關系
- Java中的類PhantomReference表示虛引用
4.3 對象死亡過程
注意:即使在可達性分析算法中不可達的對象,也不是“非死不可”,此時處于“緩刑”階段
至少要經過兩次標記過程:
- 如果對象在進行可達性分析時,沒有可達GC Root的鏈接,那么將會被標記一次并且進行篩選
- 條件? 是否有必要執行 finalize方法
- 沒必要執行
沒有覆蓋finalize 方法
finalize 方法已經被虛擬機調用過 - 有必要執行
1.將對象放置在F-Queue的隊列中,稍后由虛擬機創建線程去執行
2.不承諾等待線程運行結束
3.finalize 是對象逃脫死亡的唯一 一次機會 finalize 方法中重新引用上新鏈接,則跳出標記隊列
4.GC 進行再次標記
5.回收
- 沒必要執行
另外 方法區也是可以回收的 但僅是可以
5.GC 算法
-
標記清除
標記需要回收的對象、清除被標記的對象、簡單、直接
缺點:容易造成內存碎片
-
復制算法
將內存先一分為兩、每次只用其中一塊內存,用完后還存活的對象復制到另一塊內存中
缺點:內存利用率低 -
標記-整理算法
- 先將已死對象標記,并清除,然后將還存活的對象都向一端移動,然后清理調邊界意外的內存,保證了內存的連續性
-
分代算法
- 將存活對象分代
- 新生代
- 老年代
- 將存活對象分代
6.GC 算法實現細節
- 理論上通過GC 算法和垃圾回收標準 就可以進行正確的垃圾回收,不過還是有一些執行細節需要考量
- 由可達性分析引出GC Root,在實際生產中,GC Root 主要是在 < 全局的引用> 與< 執行上下文> 中,需要逐個檢查會消耗很多時間
- 另外可達性分析 還對 GC 停頓敏感,某一時刻的可達性是需要從全局來確認,表示引用關系不斷變化的話 是不能進行可達性分析的
這一點導致 GC 時需要 Stop The World 的重要原因
解決方案:精確式GC 直接通過OopMap的數據結構來達到目的
HotSpot 在“ 特定的位置”記錄OopMap的信息,這些位置叫做《安全點》
- 程序并不是什么地方都能停下來,只有在安全的時才能暫停
- 安全點方案
? 1.搶先式中斷:不需要線程的執行代碼主動區配合,在GC發生時,首先把所有線程全部中斷,如果由線程不在安全點,則讓它“跑”到安全點 。 現在幾乎沒有使用這種方式了
? 2.主動式中斷:
? 1.設置中斷標記
? 2.線程執行時主動區輪詢這個標志,為真時則自動掛起
? 3.輪詢標志和安全點時重合的
安全區域
? 指的時在一段代碼中,引用關系不會發生變化,在這個區域內任意地方開始GC 都是安全的
7.垃圾收集器
從上面的垃圾收集的方法論,那么下面就是垃圾收集的實際模型了,下面來一張基于1.7 JDK的垃圾收集器種類
從圖中可以看出來:
7.1 新生代中使用的
- Serial(標記復制)
- ParNew (Serial 的多線程版)
- Parallel Scavenge(復制算法,多線程)被稱為“吞吐量優先”收集器
- 關注于達到一個可控制的吞吐量(=運行用戶代碼時間 / (運行用戶代碼時間+垃圾收集時間))
- 其他收集器關注:縮短垃圾收集時用戶線程的停頓時間
7.2 老年代中使用的
- Serial Old(標記-整理)
- ParNew Old
- Parallel Old 復制算法,多線程)
- CMS (Concurrent Mark Sweep) 是一種獲取最短回收停頓時間為目標的收集器
下面主要詳細的看看CMS、G1 兩種垃圾收集器的一些細節
7.3 CMS (Concurrent Mark Sweep)
注意CMS 使用的使用標記-清除算法,它的實現主要是有四個步驟。
7.3.1 四個步驟
- 初始標記
- 需要“Stop The World”
- 僅只是標記一下GC Roots 能直接關聯到的對象
- 并發標記
- 進行GC Root Tracing 的過程
- 重新標記
- 仍需要“Stop The World”
- 修正并發標記期間因用戶程序運作而導致標記記錄產生變動的那一部分對象的標記記錄
- 并發清除
從上面的四個過程中可以發現初始標記、重新標記仍然需要“Stop The World”,初始標記僅是標記一下GC Roots n能直接關聯到的對象,速度很快,并發標記階段就是進行GC Roots Tracing 的過程,而重新標記階段是為了修正那些之前已經被標記的對象,不能處理新產生的垃圾對象。
所以CMS也有一些缺點:
- 對CPU資源非常敏感,并發時會占用一部分CPU資源
- CMS 默認啟動的回收線程數:(CPU數量 +3)/ 4,在4CPU以上時,并發回收垃圾收集線程不少于25%,并隨著CPU的數量增加而下降,當不足4個(如2個),CMS對用戶線程影響可能變得很大。
- 無法處理浮游垃圾 “Floating Garbage”
- 指的是在CMS 并發清理階段用戶線程還在運行著,并伴隨產生的新的垃圾,這部分的垃圾并未被標記,所以不會被處理
- 針對上面的現象,JVM 提供一個參數-XX:CMSInitiatingOccupancyFraction,內存使用率到達該值就會觸發GC ,以便降低內存回收的次數
- 可能導致“Concurrent Mode Failure” ,JVM 會啟動后備方案:臨時啟動Serial Old收集器,停頓時間會很長
- 標記-清除方法會導致內存碎片化
- 默認提供一個參數:-XX:+UseCMSCompactAtFullCollection (默認開啟),用于內存碎片的合并整理
- -XX:CMSFullGCsBeforeCompaction ( 默認值0) 執行多少次不壓縮的Full GC后,跟著來一次帶壓縮的GC
7.4 G1
G1是一款面向服務端應用的垃圾收集器。其具備以下的特點:
-
并行與并發
- 充分利用多CPU、多核環境下的硬件優勢,使用多個CPU來縮減Stop-The-World的停頓時間,部分收集器需要停頓Java線程執行的GGC動作,G1 仍可以通過并發的方式讓Java程序繼續執行
-
分代收集
- 分代概念在G1中依然保留,雖然G1可以不需要其他收集器配合就能獨立管理整個GC堆,但它能夠采用不太的方式去處理新建的對象和已經存活了一段時間、熬過多次GC的就對象以獲取更好的收集效果
-
空間整合
- G1整體上來看是基于“標記-整理”算法實現的
- 從局部(兩個Region之間)上來看是基于“復制”算法實現的
- 綜上G1不會產生內存空間碎片
-
可預測的停頓
- 降低停頓時間是G1和CMS的共同關注點,但G1除了追求低停頓外,還能建立可預測的停頓時間模型,能讓使用者明確指定一個長度為M毫秒的時間片段內,消耗在垃圾收集器上的時間不得超過N毫秒
之前的收集器的范圍都是整個新生代或者整個老年代,而G1不再是這樣。G1收集器使用是,Java堆的內存布局就與其他收集器有很大的差別。
它將整個Java堆劃分為多個大小相等的獨立區域(Region),雖然留有新生代和老年代的概念,但是新生代和老年代不再是物理隔離的了,它們都是一部分Region(不需要連續)的集合。通過這種方式,G1跟蹤各個Region里面的垃圾堆積的價值大小(回收所獲得的空間大小以及回收所需時間的經驗值),在后臺維護一個優先列表,每次根據允許的收集時間,優先回收價值最大的Region(這就是Garbage-First名稱的由來),其大致可劃分以下幾個步驟:
- 初始標記
- 并發標記
- 最終標記
- 篩選回收
7.5 擴展 ZGC
? ZGC全稱是Z Garbage Collector,是一款可伸縮(scalable)的低延遲(low latency garbage)、并發(concurrent)垃圾回收器,旨在實現以下幾個目標:
- 停頓時間不超過10ms
- 停頓時間不隨heap大小或存活對象大小增大而增大
- 可以處理從幾百兆到幾T的內存大小
-server -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -Xlog:age*,gc*=info:file=gc-%t.log:time,tid,tags:filecount=3,filesize=20m -Djava.io.tmpdir=/tmp'
ZGC為什么可以這么優秀,主要是因為以下幾個特性。
- Concurrent
- ZGC只有短暫的STW,大部分的過程都是和應用線程并發執行,比如最耗時的并發標記和并發移動過程。
- Region-based
- ZGC中沒有新生代和老年代的概念,只有一塊一塊的內存區域page,以page單位進行對象的分配和回收。
- Compacting
- 每次進行GC時,都會對page進行壓縮操作,所以完全避免了CMS算法中的碎片化問題。
- NUMA-aware
- 現在多CPU插槽的服務器都是Numa架構,比如兩顆CPU插槽(24核),64G內存的服務器,那其中一顆CPU上的12個核,訪問從屬于它的32G本地內存,要比訪問另外32G遠端內存要快得多。
- Using colored pointers
- 和以往的標記算法比較不同,CMS和G1會在對象的對象頭進行標記,而ZGC是標記對象的指針。
- 其中低42位對象的地址,42-45位用來做指標標記。
- Using load barriers
- 因為在標記和移動過程中,GC線程和應用線程是并發執行的,所以存在這種情況:對象A內部的引用所指的對象B在標記或者移動狀態,為了保證應用線程拿到的B對象是對的,那么在讀取B的指針時會經過一個 “load barriers” 讀屏障,這個屏障可以保證在執行GC時,數據讀取的正確性。
ZGC默認支持NUMA架構,在創建對象時,根據當前線程在哪個CPU執行,優先在靠近這個CPU的內存進行分配,這樣可以顯著的提高性能,在SPEC JBB 2005 基準測試里獲得40%的提升。
ZGC默認支持NUMA架構,在創建對象時,根據當前線程在哪個CPU執行,優先在靠近這個CPU的內存進行分配,這樣可以顯著的提高性能,在SPEC JBB 2005 基準測試里獲得40%的提升。
ZGC目前只在Linux/x64上可用,如果有足夠的需求,將來可能會增加對其他平臺的支持。
智能推薦
jvm學習之一GC簡介
1.如何判斷對象為垃圾對象 為了查看垃圾回收的信息 -verbose:gc -XX:+PrintGCDetails 就可以看到gc信息 1.1引用計數法 在對象中添加一個引用計數器,當有地方引用這個對象的時候,引用計數器的值就+1,當引用失效的時候,計數器的值就減-1. 在java中是通過引用來和對象進行關聯的,也就是說如果要操作對象,必須通過引用來進行。那么很顯然一個...
freemarker + ItextRender 根據模板生成PDF文件
1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...
電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!
Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...
requests實現全自動PPT模板
http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...
猜你喜歡
Linux C系統編程-線程互斥鎖(四)
互斥鎖 互斥鎖也是屬于線程之間處理同步互斥方式,有上鎖/解鎖兩種狀態。 互斥鎖函數接口 1)初始化互斥鎖 pthread_mutex_init() man 3 pthread_mutex_init (找不到的情況下首先 sudo apt-get install glibc-doc sudo apt-get install manpages-posix-dev) 動態初始化 int pthread_...
統計學習方法 - 樸素貝葉斯
引入問題:一機器在良好狀態生產合格產品幾率是 90%,在故障狀態生產合格產品幾率是 30%,機器良好的概率是 75%。若一日第一件產品是合格品,那么此日機器良好的概率是多少。 貝葉斯模型 生成模型與判別模型 判別模型,即要判斷這個東西到底是哪一類,也就是要求y,那就用給定的x去預測。 生成模型,是要生成一個模型,那就是誰根據什么生成了模型,誰就是類別y,根據的內容就是x 以上述例子,判斷一個生產出...
styled-components —— React 中的 CSS 最佳實踐
https://zhuanlan.zhihu.com/p/29344146 Styled-components 是目前 React 樣式方案中最受關注的一種,它既具備了 css-in-js 的模塊化與參數化優點,又完全使用CSS的書寫習慣,不會引起額外的學習成本。本文是 styled-components 作者之一 Max Stoiber 所寫,首先總結了前端組件化樣式中的最佳實踐原則,然后在此基...