文章出處

 
一、JVM是什么
Java虛擬機(英語:Java Virtual Machine,縮寫為JVM),又名爪哇虛擬器,一種能夠運行Java bytecode的虛擬機,以堆棧結構機器來進行實做。最早由太陽微系統所研發并實現第一個實現版本,是Java平臺的一部份,能夠運行以Java語言寫作的軟件程序。
 
Java虛擬機有自己完善的硬體架構,如處理器、堆棧、寄存器等,還具有相應的指令系統。JVM屏蔽了與具體操作系統平臺相關的信息,使得Java程序只需生成在Java虛擬機上運行的目標代碼(字節碼),就可以在多種平臺上不加修改地運行。通過對中央處理器(CPU)所執行的軟件實作,實現能執行編譯過的Java程序碼(Applet與應用程序)。
 
作為一種編程語言的虛擬機,實際上不只是專用于Java語言,只要生成的編譯文件符合JVM對載入編譯文件格式要求,任何語言都可以由JVM編譯運行。除外,除了甲骨文,也有其他開源或閉源的實現。
——維基百科
這個描述還是很簡單易懂的,虛擬機的這種機制帶給了代碼一種全新的生命力,就是一處編繹,到處運行。當然美好的事情總歸是有些缺陷的。因為要在一臺物理機器上搭建一套虛擬的體系,以此來解決各個硬件與系統間的差異問題,確實是件很美好的事情,但同時損失的自然就是運行時的效率。
 
二、JVM的體系規格
java的這套體系是種開放的規格,只要能按規格編繹出來的程序都可以跑在JVM上。JVM定義了控制Java代碼解釋執行和具體實現的五種規格,它們是:
  • JVM指令系統 
  • JVM寄存器 
  • JVM 棧結構 
  • JVM 碎片回收堆 
  • JVM 存儲區
 
三、JVM的工作原理
 
  • 操作系統加載JVM(windows)
1.創建JVM裝載環境和配置
2.裝載JVM.dll
3.初始化JVM.dll并掛界到JNIENV(JNI調用接口)實例
4.調用JNIEnv實例裝載并處理class類
  • JVM加載類
下面看看一個java代碼是怎么運行起來的:
 

 

四、JVM的內存管理
這里有一篇文章講的很詳細:http://developer.51cto.com/art/201303/387175.htm
 
JVM的內存結構分為6塊:PC Register(PC寄存器)、JVM堆、JVM棧、方法區域、運行時常量、本地方法堆棧。如下圖示意:
對于開發來說主要關注的還是堆和棧。
 
JVM一些參數設置:
-Xss:這個參數就是用來指定棧的大小
-Xms:設置JVM啟動時最小的堆內存大小
-Xmx:設置JVM堆的最大內存大小
-XX:PermSize及-XX:MaxPermSize指定方法區域(MethodArea)的初始值與最大值
 
MethodArea對應的是持久代(PermanetGeneration),所以設置Perm的大小很重要,否則會報java.lang.OutOfMemoryError: PermGen space。
 
五、垃圾收集器(Garbage Collector,GC)
垃圾收集器這個東西對于程序員來說可謂是一種解脫,可以不用顯式釋放內存了。這種神奇的療效還是要看看他的基本原理了解一下情況才行。下面摘了一段話:
有幾種垃圾收集的基本策略:引用計數、標記-清除、標記-整理 (mark-compact) 和復制。此外,一些算法可以以 增量 方式完成它們的工作(不需要一次收集整個堆,使得收集暫停時間更短),一些算法可以在用戶程序運行時運行( 并發收集)。其他算法則必須在用戶程序暫停時一次進行整個收集(即所謂的 stop-the-world收集器)。最后,還有混合型的收集器,如 1.2 和以后版本的 JDK 使用的分代收集器,它對堆的不同區域使用不同的收集算法。
——摘自developerWorks 中國
這里就回收的算法來看主要就上面列出來的幾種,其中比較重要的是“復制”和“標識-整理”有必要理解一下。
 
復制收集器就是劃分兩個對等空間,其中一個放活躍對象,另一個空著,等第一個滿了就復制活躍對象到另一個空著的,然后將這兩個空間角色換一下。這里有個重點就是只復制活躍對象。活躍對象通常就是可到達的對象也就是不用回收的內存,換言之就是除此之外的就是垃圾,那么這樣的好處就是復制一次后,將那些垃圾一次回收就行了。而且復制后內存空間是會經過整理的連續的,不會有碎片問題。
 
標識-整理收集器算法結合了標記-清除和復制,代價是增加了一些收集復雜性。與標記-清除類似,標記-整理是兩階段過程,在標記階段訪問并標記每個活躍對象。然后,復制標記的對象,使所有活躍對象被整理到堆的底部。如果每一次收集時進行徹底的整理,那么得到的堆就類似于復制收集器的結果 ―― 在堆的活躍部分與自由部分有明確的界線,這樣分配成本與復制收集器相當。長壽的對象趨向于沉在堆的底部,這樣就不會像在復制收集器中那樣反復復制它們。
 
既然是垃圾回收,那么自然有一個問題什么是垃圾?
由分配器分配的,但是用戶程序不可到達的內存塊。不可到達是什么意思?可以以兩種方式之一訪問內存塊 ―― 或者用戶程序在 根 (root)中有對這一內存塊的引用,或者在另一個可到達的塊中有對這個塊的引用。
——摘自developerworks
這里面提到了一個很關鍵的點,就是根(Root),那什么才是Root?這個得好好了解一下。下面是GC Root的種類:
  • Class - 由系統類加載器(system class loader)加載的對象,這些類是不能夠被回收的,他們可以以靜態字段的方式保存持有其它對象。我們需要注意的一點就是,通過用戶自定義的類加載器加載的類,除非相應的java.lang.Class實例以其它的某種(或多種)方式成為roots,否則它們并不是roots,. 
  • Thread - 活著的線程 
  • Stack Local - Java方法的local變量或參數 
  • JNI Local - JNI方法的local變量或參數 
  • JNI Global - 全局JNI引用 
  • Monitor Used - 用于同步的監控對象 
  • Held by JVM - 用于JVM特殊目的由GC保留的對象,但實際上這個與JVM的實現是有關的。可能已知的一些類型是:系統類加載器、一些JVM知道的重要的異常類、一些用于處理異常的預分配對象以及一些自定義的類加載器等。然而,JVM并沒有為這些對象提供其它的信息,因此就只有留給分析分員去確定哪些是屬于"JVM持有"的了。
分代垃圾收集
有人做過分析,應用程序堆中的對象有98%的對象存活的時間比較短,還有一些是會存活很長,甚至會隨著應用程序整個生命周期。將這兩類對象可以稱為年輕代和持久代。
分代收集器(generializational collector)將堆分為多個代。在年輕的代中創建對象,滿足某些提升標準的對象,如經歷了特定次數垃圾收集的對象,將被提升到下一更老的代。分代收集器對不同的代可以自由使用不同的收集策略,對各代分別進行垃圾收集。
——摘自developerworks
可以看出分代垃圾收集主要是將對象以生命周期進行歸類,然后針對不同的類別使用不同的回收算法,這樣就可以更優的進行跟蹤回收。
 
JDK 1.4.1 默認收集器

在默認情況下,JDK 1.4.1 將堆分為兩部分,一個年輕的代和一個老的代(實際上,還有第三部分――永久空間,它用于存儲裝載的類和方法對象)。借助于復制收集器,年輕的代又分為一個創建空間(通常稱為 Eden)和兩個生存半空間。

老的代使用標記-整理收集器。對象在經歷了幾次復制后提升到老的代。小的收集將活的對象從 Eden 和一個生存半空間復制到另一個生存半空間,并可能提升一些對象到老的代。大的收集(major collection)既會收集年輕的代,也會收集老的代。 System.gc() 方法總是觸發一個大的收集,這就是應該盡量少用(如果不能完全不用的話) System.gc() 的原因之一,因為大的收集要比小的收集花費長得多的時間。沒有辦法以編程方式觸發小的收集。

——摘自developerworks

總的機制就是讓壽命長的對象逐步往老的代中放,這樣可以優化收集的時機,減少收集暫停給應用程序帶來的影響基本上GC的簡單機制就這些內容。
 
參考文:
 

不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 AutoPoster 的頭像
    AutoPoster

    互聯網 - 大數據

    AutoPoster 發表在 痞客邦 留言(0) 人氣()