Java GC

Java技術體系

從廣義上講,Clojure、JRuby、Groovy等運行於Java虛擬機上的語言及其相關的程序都屬於Java技術體系中的一員。若是僅從傳統意義上來看,Sun官方所定義的Java技術體系包括如下幾個組成部分:html

  • Java程序設計語言
  • 各類硬件平臺上的Java虛擬機
  • Class文件格式
  • Java API類庫
  • 來自商業機構和開源社區的第三方Java類庫

咱們能夠把Java程序設計語言、Java虛擬機、Java API類庫這三部分統稱爲JDK(Java Development Kit),JDK是用於支持Java程序開發的最小環境。java

另外,能夠把Java API類庫中的Java SE API子集[1]和Java虛擬機這兩部分統稱爲JRE(Java Runtime Environment),JRE是支持Java程序運行的標準環境。web

圖片描述

以上是根據各個組成部分的功能來進行劃分的,若是按照技術所服務的領域來劃分,或者說按照Java技術關注的重點業務領域來劃分,Java技術體系能夠分爲4個平臺,分別爲:算法

  • Java Card:支持一些Java小程序(Applets)運行在小內存設備(如智能卡)上的平臺。
  • Java ME(Micro Edition):支持Java程序運行在移動終端(手機、PDA)上的平臺,對Java API有所精簡,並加入了針對移動終端的支持,這個版本之前稱爲J2ME。
  • Java SE(Standard Edition):支持面向桌面級應用(如Windows下的應用程序)的Java平臺,提供了完整的Java核心API,這個版本之前稱爲J2SE。
  • Java EE(Enterprise Edition):支持使用多層架構的企業應用(如ERP、CRM應用)的Java平臺,除了提供Java SE API外,還對其作了大量的擴充[3]並提供了相關的部署支持,這個版本之前稱爲J2EE。

JVM

Java Virtual Machine,Java虛擬機。是java編譯後的.class文件(字節碼文件)與硬件系統之間的接口,也就是說用來運行.class文件。JVM實現了Java最重要的特性:平臺無關性spring

編譯後的 Java 程序指令並不直接在硬件系統的 CPU 上執行,而是由 JVM 執行,JVM屏蔽了與具體平臺相關的信息。JVM對字節碼文件進行解釋執行,把字節碼翻譯成相關平臺上的機器指令。

javac 是收錄於 JDK 中的 Java 語言編譯器。咱們使用javac命令編譯Java源文件,獲得.class文件。而後使用java命令執行.class文件(也就是使用JVM運行.class文件)。使用jar命令可對字節碼文件以及配置文件進行打包(可對一個由多個字節碼文件和配置文件等資源文件構成的項目進行打包)。小程序

因此不熟悉java的同窗要注意了,JVM並非用來編譯和執行java的,JVM只負責執行字節碼文件,編譯java文件由 javac來完成

java有一套公用的規範:Java Language and Virtual Machine Specifications服務器

目前有三大Java虛擬機:HotSpot,oracle JRockit,IBM J9。架構

JRockit是oracle發明的,用於其WebLogic服務器,IBM JVM是IBM發明的用於其Websphere服務器,不一樣的JDK可能存在兼容性問題。oracle

JRockit和J9不存在永久代這種說法。這裏只討論HotSpot虛擬機,這也是目前使用的最多的JVM。Sun JDK7 HotSpot虛擬機的內存模型以下圖所示:spa

JVM的內存可分爲:線程棧、堆、靜態方法區,native方法使用的是直接內存,不包含在JVM中。Java NDK能夠調用C/C++。

方法區

方法區是可供各線程共享的運行時內存區域

方法區(Method Area)與Java堆同樣,是各個線程共享的內存區域,它用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。雖然Java虛擬機規範把方法區描述爲堆的一個邏輯部分,可是它卻有一個別名叫作Non-Heap(非堆),目的應該是與Java堆區分開來。

在不一樣的JDK版本中,方法區中存儲的數據是不同的。在JDK1.6及以前,運行時常量池是方法區的一個部分,同時方法區裏面存儲了類的元數據信息、靜態變量、即時編譯器編譯後的代碼(好比spring 使用IOC或者AOP建立bean時,或者使用cglib,反射的形式動態生成class信息等)等。在JDK1.7及之後,JVM已經將運行時常量池從方法區中移了出來,在JVM堆開闢了一塊區域存放常量池。

方法區和永久代的關係

方法區(method area)只是JVM規範中定義的一個概念,用於存儲類信息、常量池、靜態變量、JIT編譯後的代碼等數據,具體放在哪裏,不一樣的實現能夠放在不一樣的地方。

永久代是Hotspot虛擬機特有的概念,是方法區的一種實現,別的JVM都沒有這個東西。在Java 8中,永久代被完全移除,取而代之的是另外一塊與堆不相連的本地內存——元空間(Metaspace),‑XX:MaxPermSize 參數失去了意義,取而代之的是-XX:MaxMetaspaceSize。

永久代(PermGen)包含了JVM須要的應用元數據,這些元數據描述了在應用裏使用的類和方法。注意,永久代不是Java堆內存的一部分。永久代存放JVM運行時使用的類。永久代一樣包含了Java SE庫的類和方法。永久代的對象在full GC時進行垃圾收集。

對於習慣在HotSpot虛擬機上開發、部署程序的開發者來講,不少人都更願意把方法區稱爲「永久代」(Permanent Generation),本質上二者並不等價,僅僅是由於HotSpot虛擬機的設計團隊選擇把GC分代收集擴展至方法區,或者說使用永久代來實現方法區而已,這樣HotSpot的垃圾收集器能夠像管理Java堆同樣管理這部份內存,可以省去專門爲方法區編寫內存管理代碼的工做。對於其餘虛擬機(如BEA JRockit、IBM J9等)來講是不存在永久代的概念的。原則上,如何實現方法區屬於虛擬機實現細節,不受虛擬機規範約束,不一樣的JVM廠商,針對本身的JVM可能有不一樣的方法區實現方式。但使用永久代來實現方法區,如今看來並非一個好主意,由於這樣更容易遇到內存溢出問題(永久代有-XX:MaxPermSize的上限,J9和JRockit只要沒有觸碰到進程可用內存的上限,例如32位系統中的4GB,就不會出現問題),並且有極少數方法(例如String.intern())會因這個緣由致使不一樣虛擬機下有不一樣的表現。所以,對於HotSpot虛擬機,根據官方發佈的路線圖信息,如今也有放棄永久代並逐步改成採用Native Memory來實現方法區的規劃了,在目前已經發布的JDK 1.7的HotSpot中,已經把本來放在永久代的字符串常量池移出。

也就是說永久代是JVM規範中方法區的一種實現方式

Java虛擬機規範對方法區的限制很是寬鬆,除了和Java堆同樣不須要連續的內存和能夠選擇固定大小或者可擴展外,還能夠選擇不實現垃圾收集。相對而言,垃圾收集行爲在這個區域是比較少出現的,但並不是數據進入了方法區就如永久代的名字同樣「永久」存在了。這區域的內存回收目標主要是針對常量池的回收和對類型的卸載,通常來講,這個區域的回收「成績」比較難以使人滿意,尤爲是類型的卸載,條件至關苛刻,可是這部分區域的回收確實是必要的。在Sun公司的BUG列表中,曾出現過的若干個嚴重的BUG就是因爲低版本的HotSpot虛擬機對此區域未徹底回收而致使內存泄漏。

根據Java虛擬機規範的規定,當方法區沒法知足內存分配需求時,將拋出OutOfMemoryError異常。

  1. 堆大小=新生代+老年代。默認狀況下,新生代(Young generation)佔$\frac{1}{3}$的堆空間大小,老年代(Old generation)佔$\frac{2}{3}$的堆空間大小。
  2. 新生代被細分爲一個Eden(伊甸園)和兩個Survivor區域,這兩個Survivor區域分別被命名爲fromto以示區分。默認狀況下,它們的空間大小關係是Eden:from:to=8:1:1
  3. JVM每次只會使用Eden和其中一塊Survivor區域來爲對象服務,因此不管何時,總有一塊Survivor區域空閒着。所以,新生代實際上能夠用的內存空間爲90%的新生代空間。

下面要講的GC(garbage collection)就是針對進行內存回收。

GC的策略

  1. 分區的目的是爲了方便對不一樣的區採起不一樣的回收策略。GC分爲兩種:新生代中的Minor GC,老生代中的Major GC;兩個加在一塊兒叫作Full GC,因爲Major GC的前提是Minor GC,因此發生Major GC就必定表明着Full GC,。新生代是GC收集垃圾的頻繁區域。
  2. 數據會首先分配到Eden區中(固然也有特殊狀況,若是是大對象那麼會直接放到老年代(大對象是指須要大量連續內存的java對象)),當Eden沒有足夠的空間的時候就會觸發JVM發起一次Minor GC。若是對象經歷一次Minor GC還存活,而且又能被Survivor空間接受,那麼將被移動到Survivor空間當中,並將其年齡設爲1,對象在Survivor中每熬過一次Minor GC,年齡就加1,當年齡達到必定的程度(默認爲15)時,就被晉升到老年代中了,固然晉升老年代的年齡是能夠設置的。新生代是GC收集垃圾的頻繁區域。
  3. 新生代主要存放的是那些很快就會被GC回收掉的或者不是特別大的對象(要看你是否設置了-XX:PretenureSizeThreshold參數了)。-XX:PretenureSizeThreshold 的默認值和做用
  4. -Xms2000m -Xmx2000m -Xmn800m -XX:PermSize=64m -XX:MaxPermSize=256m -Xss1M Xms是JVM初始堆的大小,Xmx是JVM最大堆大小,Xmn是新生代大小,PermSize是永久代的初始大小,MaxPermSize是永久代的最大大小,Xss是每一個線程棧的大小。JVM系列三:JVM參數設置、分析

    • Xms64m or -Xms64M
    • Xmx1g or -Xmx1G
    • Can also use 2048MB to specify 2GB
    • Also, make sure you just use whole numbers when specifying your arguments. Using -Xmx512m is a valid option, but -Xmx0.5g will cause an error.
  5. 虛擬機初始化時已經設定了各部分的內存大小,分爲三部分:

    • 新生代:新建立的對象
    • 老年代:通過屢次垃圾回收沒有被回收的對象或者太大的對象
    • 永久代:JVM自身使用的內存,包含類信息等

新生代策略

新生代採用複製算法。將新生代分爲3個區:較大的Eden和兩個較小的Survivor。發生在新生代的GC爲Minor GC,在Minor GC時會將新生代中還存活着的對象複製進一個Survivor中,而後對Eden和另外一個Survivor進行清理。因此日常可用的新生代大小爲Eden大小+一個Survivor大小。

相關文章
相關標籤/搜索