##############部分摘自深入Java虛擬機###########################
數據類型:
Java虛擬機中數據類型可以分爲兩類:基本類型和引用類型。
基本類型包括:byte,short,int,long,char,float,double,Boolean,returnAddress
引用類型包括:類類型,接口類型和數組。
堆與棧:
棧是運行時的單位,而堆是儲存單位。
在Java中一個線程就會相應有一個線程棧與之對應,這點很容易理解,因爲不同的線程執行邏輯有所不同,因此需要一個獨立的線程棧。
而堆則是所有線程共享的。棧因爲是運行單位,因此裏面存儲的信息都是跟當前線程(或程序)相關信息的。包括局部變量、程序運行狀態、方法返回值等等;
而堆只負責存儲對象信息。面向對象就是堆與棧完美的結合。
在Java中,Main函數就是棧的起始點,也是程序的起始點。
堆中存的是對象。棧中存的是基本數據類型和堆中對象的引用。一個對象的大小是不可估計的,或者說是可以動態變化的,但是在棧中,
一個對象只對應了一個4btye的引用。
(堆棧分離的好處)。
Java中的參數傳遞時傳值呢?還是傳引用?
1. 不要試圖與C進行類比,Java中沒有指針的概念
2. 程序運行永遠都是在棧中進行的,因而參數傳遞時,只存在傳遞基本類型和對象引用的問題。不會直接傳對象本身。
Java在方法調用傳遞參數時,因爲沒有指針,所以它都是進行傳值調用。
Java中,棧的大小通過-Xss來設置,當棧中存儲數據比較多時,需要適當調大這個值,否則會出現java.lang.StackOverflowError異常。
在Java中,一個空Object對象的大小是8byte,這個大小隻是保存堆中一個沒有任何屬性的對象的大小。
Object oj = new Object(); 佔用空間:4byte(棧中保存引用) + 8byte(堆中對象信息)
Java在對對象內存分配時都是以8的整數倍來分。
一個基本類型包裝類的大小至少是16byte
對象引用類型分爲強引用、軟引用、弱引用和虛引用
垃圾回收:
分代的垃圾回收策略(1.2開始),是基於這樣一個事實:不同的對象的生命週期是不一樣的。因此,不同生命週期的對象可以採取不同的收集方式,以便提高回收效率。
虛擬機中的共劃分爲三個代:年輕代(Young Generation)、年老代(Old Generation)和持久代(Permanent Generation)。
其中持久代主要存放的是Java類的類信息,與垃圾收集要收集的Java對象關係不大。年輕代和年老代的劃分是對垃圾收集影響比較大的。
年輕代:
所有新生成的對象首先都是放在年輕代的。年輕代的目標就是儘可能快速的收集掉那些生命週期短的對象。年輕代分三個區。一個Eden區,
兩個Survivor區(一般而言)當Eden區滿時,還存活的對象將被複制到Survivor區(兩個中的一個),當這個Survivor區滿時,此區的存活對象將被複制到
另外一個Survivor區,當這個Survivor去也滿了的時候,從第一個Survivor區複製過來的並且此時還存活的對象,將被複制“年老區(Tenured)”。
需要注意,Survivor的兩個區是對稱的,沒先後關係,所以同一個區中可能同時存在從Eden複製過來 對象,和從前一個Survivor複製過來的對象,
而複製到年老區的只有從第一個Survivor去過來的對象。而且,Survivor區總有一個是空的。同時,根據程序需要,Survivor區是可以配置爲多個的
(多於兩個),這樣可以增加對象在年輕代中的存在時間,減少被放到年老代的可能。
年老代:
在年輕代中經歷了N次垃圾回收後仍然存活的對象,就會被放到年老代中。因此,可以認爲年老代中存放的都是一些生命週期較長的對象。
持久代:
用於存放靜態文件,如今Java類、方法等。持久代對垃圾回收沒有顯著影響,但是有些應用可能動態生成或者調用一些class,例如Hibernate等,
在這種時候需要設置一個比較大的持久代空間來存放這些運行過程中新增的類。持久代大小通過-XX:MaxPermSize=<N>進行設置。
何時觸發垃圾回收:
由於對象進行了分代處理,因此垃圾回收區域、時間也不一樣。GC有兩種類型:Scavenge GC和Full GC。
Scavenge GC:
一般情況下,當新對象生成,並且在Eden申請空間失敗時,就會觸發Scavenge GC,對Eden區域進行GC,清除非存活對象,並且把尚且存活的對象移動
到Survivor區。
Full GC:
對整個堆進行整理,包括Young、Tenured和Perm。Full GC因爲需要對整個對進行回收,所以比ScavengeGC要慢,因此應該儘可能減少Full GC的次數。
在對JVM調優的過程中,很大一部分工作就是對於FullGC的調節。有如下原因可能導致Full GC:
· 年老代(Tenured)被寫滿
· 持久代(Perm)被寫滿
· System.gc()被顯示調用
·上一次GC之後Heap的各域分配策略動態變化
選擇合適的垃圾收集算法:
JDK5.0以前都是使用串行收集器
串行收集器 可以使用-XX:+UseSerialGC打開
並行收集器 XX:+UseParallelGC.打開 -XX:+UseParallelOldGC打開 使用-XX:ParallelGCThreads=<N>設置並行垃圾回收的線程數
併發收集器CMS 使用-XX:+UseConcMarkSweepGC打開
JVM中最大堆大小有三方面限制:相關操作系統的數據模型(32-bt還是64-bit)限制;系統的可用虛擬內存限制;系統的可用物理內存限制。
32位系統下,一般限制在1.5G~2G;64爲操作系統對內存無限制。在Windows Server 2003 系統,3.5G物理內存,JDK5.0下測試,最大可設置爲1478m。
java -Xmx3550m -Xms3550m -Xmn2g –Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0
-Xmx3550m:設置JVM最大可用內存爲3550M
-Xms3550m:設置JVM初始內存爲3550m
-Xmn2g:設置年輕代大小爲2G。整個堆大小=年輕代大小 + 年老代大小 + 持久代大小。持久代一般固定大小爲64m,所以增大年輕代後,將會減小年老代大
小。
此值對系統性能影響較大,Sun官方推薦配置爲整個堆的3/8。
-Xss128k:設置每個線程的堆棧大小。JDK5.0以後每個線程堆棧大小爲1M,以前每個線程堆棧大小爲256K。更具應用的線程所需內存大小進行調整。
在相同物理內存下,減小這個值能生成更多的線程。但是操作系統對一個進程內的線程數還是有限制的,不能無限生成,經驗值在3000~5000左右。
-XX:NewRatio=4:設置年輕代(包括Eden和兩個Survivor區)與年老代的比值(除去持久代)。設置爲4,則年輕代與年老代所佔比值爲1:4,年輕代佔整個
堆棧的1/5
-XX:SurvivorRatio=4:設置年輕代中Eden區與Survivor區的大小比值。設置爲4,則兩個Survivor區與一個Eden區的比值爲2:4,一個Survivor區佔整個年輕代
的1/6
-XX:MaxPermSize=16m:設置持久代大小爲16m。
-XX:MaxTenuringThreshold=0:設置垃圾最大年齡。如果設置爲0的話,則年輕代對象不經過Survivor區,直接進入年老代。對於年老代比較多的應用,可以
提高效率。
如果將此值設置爲一個較大值,則年輕代對象會在Survivor區進行多次複製,這樣可以增加對象再年輕代的存活時間,增加在年輕代即被回收的概論。
垃圾回收統計信息
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename
補充:
- Serial:串行收集器,當進行垃圾收集時,會暫停所有線程
- Parallel:並行收集器,是串行收集器的多線程版本,多CPU下
- ParallelOld:老年代的Parallel版本
- ConcMarkSweep:簡稱CMS,是併發收集器,將部分操作與用戶線程併發執行
- CMSIncrementalMode:CMS收集器變種,屬增量式垃圾收集器,在併發標記和併發清理時交替運行垃圾收集器和用戶線程
- G1:面向服務器端應用的垃圾收集器,計劃未來替代CMS收集器