<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                本篇是《深入理解Java虛擬機-Java 高級特性與最佳實踐》學習筆記,周志明著,Understanding the JVM-Advanced Features and Best Practices,機械工業出版社,2011.6出版。 重溫Java JVM知識,重點學習了與日常開發工作相關性最大的“自動化內存管理”模塊,對Java容器優化、內存問題解決很有幫助;習慣了從互聯網看電子書,難以集中和記憶,現在找幾本紙質書重溫,可以很清靜、很安靜的理解和消化,受益匪淺。 # 自動內存管理機制 Java和C++之間有一睹由動態內存分配和垃圾收集機制組成的墻,里面的人想出來,外面的人想進去。 ### 1、Java內存區域與內存溢出異常 <table border="1" width="100%" cellspacing="1" cellpadding="1"><tbody><tr bgcolor="#ff9900"><td>虛擬機運行時數據區</td><td>存儲內容</td><td>可能發生的溢出錯誤及優化措施</td><td>描述</td></tr><tr><td>堆(Heap)</td><td>對象實例</td><td>OutOfMemoryError: Java heap space,通過調節參數-Xms2048m -Xmx2048m,設置堆的初始內存和最大內存進行優化<span style="white-space:pre"/></td><td>在虛擬機啟動時創建,是VM所管理的內存中最大的一塊,是垃圾收集器管理的主要區域,也被稱作“GC堆”,堆是所有線程共享的</td></tr><tr><td>虛擬機棧(VM Stack)</td><td>Java方法執行的內存模型:每個方法被執行時,都會同時創建一個棧幀(Stack Frame)用于存儲局部變量表(基本數據類型和對象引用類型),操作數棧,動態鏈接,方法出口等信息</td><td>單線程可能會報StackOverflowError,多線程可能會報OutOfMemoryError,對應參數是-Xss2m,一般不設這個參數,而是采用默認值</td><td>生命周期和線程相同,每個方法被調用直至完成的過程,就對應著一個棧幀在虛擬機棧中入棧和出棧的過程,有時會通過減小棧和堆大小的方式優化,以保證可以開更多線程,棧是線程私有的</td></tr><tr><td>本地方法棧(Native Method Stack)</td><td colspan="3">本地方法棧和虛擬機棧所發揮的作用非常相似,其區別是:虛擬機棧為虛擬機執行Java方法(也就是字節碼)服務,而本地方法棧為虛擬機使用到的Native方法服務。<br/>在Sun HotSpot虛擬機中,本地方法棧和虛擬機棧是在一起的。</td></tr><tr><td>方法區(Method Area)</td><td>類信息、常量、靜態變量、即時編譯器編譯后的代碼</td><td>OutOfMemoryError: PermGen space,通過調節參數-XX:PermSize=1024m -XX:MaxPermSize=1024m,設置方法區的初始內存和最大內存進行優化</td><td>有個別名叫Non-Heap,對于HotSpot來說,也被稱作“永久代”(Permanent Generation),運行時常量池(Runtime Constant Pool)是方法區的一部分,方法區是所有線程共享的</td></tr><tr><td>程序計數器(Program Counter Register)</td><td>當前線程所執行的字節碼的行號計數器</td><td>不會溢出</td><td>是線程私有的</td></tr><tr><td>直接內存(Direct Memory)</td><td colspan="3">直接內存不是虛擬機運行時數據區的一部分,是使用Native函數庫直接分配的堆外內存,然后通過一個存儲在Java堆中的DirectByteBuffer對象作為這塊內存的引用,目的是在一些場景中顯著提高性能,因為避免了再Java對和Native堆中來回復制數據;可以通過-XX:MaxDirectMemorySize指定,如果不指定,則默認與Java堆的最大值(-Xmx指定)一樣;會報溢出錯誤:OutOfMemoryError</td></tr><tr bgcolor="#009900"><td>三大商業虛擬機</td><td colspan="3">Sun HotSpot,Bea Jrockit,IBM J9,前兩個虛擬機都已經被Oracle收購,Sun HotSpot是使用最普遍的虛擬機。</td></tr></tbody></table> JDK5.0以后每個線程棧大小為1M,之前每個線程棧大小為256K。 應當根據應用的線程所需內存大小進行調整。在相同物理內存下,減小這個值能生成更多的線程。但是操作系統對一個進程內的線程數還是有限制的,不能無限生成,經驗值在3000~5000左右。 需要注意的是:當這個值被設置的較大(例如>2MB)時將會在很大程度上降低系統的性能。 如果我們需要知道,JVM虛擬機的默認參數配置,可以使用命令查看: java -XX:+PrintFlagsInitial | grep Ratio 我們可以看到,新生代和老年代的默認比值是2;Survior和Eden的默認比值是8. [JVM實用參數(三)打印所有XX參數及值](http://ifeve.com/useful-jvm-flags-part-3-printing-all-xx-flags-and-their-values/) **補充一個案例,如下:** 如果寫了一個普通的死循環,沒有發現,則會占用cpu時間,但是可能不會導致內存溢出的情況,因為可能對于堆和棧空間都沒有太大影響; 但是如果寫了個遞歸死循環,循環遞歸,則馬上會報StackOverFlow,因為隨著遞歸函數的調用,大量的本地變量被聲明,很快,棧空間就滿了; 另,對于不同的JDK,可能會變化,比如String的常量String.intern(),在JDK6中存放在“方法區”的“運行時常量池”中,但是在JDK7中,就存放在堆中了,所以,很多細節也是在一直變化中的。。。 參考網址: [http://blog.csdn.net/kthq/article/details/8618052](http://blog.csdn.net/kthq/article/details/8618052) [http://www.open-open.com/lib/view/open1324736648468.html](http://www.open-open.com/lib/view/open1324736648468.html) ### 2、垃圾收集器與內存分配策略 **對象已死的確定方法:** <table border="1" width="100%" cellspacing="1" cellpadding="1"><tbody><tr><td>?</td><td>使用案例</td><td>描述</td></tr><tr><td>引用計數算法</td><td>COM、AS3、Python、Squirrel</td><td>很難解決對象之間的相互循環引用問題</td></tr><tr><td>根搜索算法</td><td>Java、C#、Lisp</td><td>?</td></tr><tr><td colspan="3">在Java語言里,可以作為GC Roots的對象包括:(虛擬機棧中的/方法區中的類靜態屬性和常量/本地方法棧中JNI)引用的對象<br/>Java對象的finalize方法,優先級低,運行代價高昂,不確定性大,無法保證各個對象的調用順序,建議大家完全可以忘記Java中還有這個方法的存在;這個方法最多只會被垃圾收集器調用一次。</td></tr></tbody></table> **垃圾收集算法:** | 標記-清除算法 | 最基礎的收集算法,后續的收集算法都是對其缺點改進而得的,主要缺點:效率問題和空間問題。 | |-----|-----| | 復制算法 | 現在的商業虛擬機都采用這種算法來回收新生代,Eden空間+2*Survivor空間,8:1,90%內存利用率。 | | 標記-整理算法 | 老年代采用這種收集算法,標記后將所有存活對象向一段移動,然后直接清理掉端邊界之外的內存。 | | 分代收集算法 | 實際就是對內存分代后,以上幾種算法的選擇。 | **垃圾收集器(垃圾收集算法是方法論,垃圾收集器是實現):** <table border="1" width="100%" cellspacing="1" cellpadding="1"><tbody><tr><td>Serial</td><td>新生代使用,單線程,Stop The World,復制算法</td></tr><tr><td>ParNew</td><td>Serial的多線程版本</td></tr><tr><td>Parallel Scavenge</td><td>新生代使用,并行,多線程,吞吐量優先,適用于在后頭運算而不需要太多交互的任務,復制算法</td></tr><tr><td>CMS</td><td>老年代使用,并發,多線程,Concurrent Mark Sweep,以獲取最短回收停頓時間為目標,標記-清除算法,它的問題是會產生內存碎片</td></tr><tr><td>Serial Old</td><td>老年代使用,單線程,標記-整理算法,主要Client模式使用,如果在Server模式使用,有兩大用途:一個是在JDK1.5及之前的版本中與Parallel Scavenge搭配使用;另一個就是作為CMS收集器的后備預案,在Concurrent Mode Failure的時候使用</td></tr><tr><td>Parallel Old</td><td>Parallel Scavenge的老年代版本,標記-整理算法</td></tr><tr><td>G1</td><td>Garbage First,收集器技術發展的最前沿成果,標記-整理算法,精確控制停頓時間,極力避免全區域垃圾收集</td></tr><tr bgcolor="#009900"><td colspan="2">并行:Parallel,不論宏觀還是微觀,都是同時進行的;<br/>并發:Concurrent,宏觀上是同時進行的,微觀上不一定是同時進行的,可能是交替執行。</td></tr></tbody></table> **JDK6默認的垃圾收集器:** | Client模式 | 默認-XX:+UseSerialGC,使用Serial+Serial Old的組合進行垃圾回收 | |-----|-----| | Server模式 | 默認-XX:+UseParallelGC,使用Parallel Scavenge+Parallel Old的組合進行垃圾回收,之前的JVM,可能是Parallel Scavenge+Serail Old,總之,隨著版本變遷,可能會有不同 | **JVM默認的客戶端還是服務器模式,取決于下面的情況:** | Architecture | CPU/RAM | OS | Default | |-----|-----|-----|-----| | i586 | Any | MS Windows | Client | | AMD64 | Any | Any | Server | | 64-bit SPARC | Any | Solaris | Server | | 32-bit SPARC | 2+ cores & > 2GB RAM | Solaris | Server | | 32-bit SPARC | 1 core or < 2GB RAM | Solaris | Client | | i586 | 2+ cores & > 2GB RAM | Linux or Solaris | Server | | i586 | 1 core or < 2GB RAM | Linux or Solaris | Client | **內存分配策略:** 對象優先在Eden分配、大對象直接進入老年代、長期存活的對象將進入老年代、動態對象年齡判定、空間分配擔保 驗證了前3條內存分配策略,在Win8.164bit及RHEL6.164bit下,和書中描述規則不一致,原因是我的i5-3210CPU被識別為AMD64,所以是默認以Server方式啟動JVM,采用ParallelGC;我在啟動JVM時,加上參數-XX:+UseSerialGC,強制使用SerialGC,則案例驗證通過;PretenureSizeThreshold,即設定直接進入老年代的大對象尺寸的參數,只對Serial和ParNew生效; 也由此可見,內存分配策略具有非常復雜的規則,不僅僅是這幾條規則所能描述的。 總結:經過半個世紀的發展,內存的動態分配和回收技術已經相當成熟;但是當需要排查各種內存溢出、內存泄露問題時,當垃圾收集成為系統達到更高并發量的瓶頸時,我們就需要對這些“自動化”的技術實施必要的監控和調節。 **查看JVM進程使用的垃圾回收器:** jinfo -flag UseSerialGC 60282 jinfo -flag UseParNewGC 60282 jinfo -flag UseParallelGC 60282 jinfo -flag UseParallelOldGC 60282 jinfo -flag UseConcMarkSweepGC 60282 在RHEL6.3虛擬機(1G內存、單核)下,全部輸出-,沒有+,不知道原因,截圖如下: ![](https://box.kancloud.cn/2016-05-27_5747b45022cdb.jpg) 在CentOS虛擬機(32G內存、8核)下,正常輸出UseParallelGC,截圖如下: ![](https://box.kancloud.cn/2016-05-27_5747b450384a7.jpg) **參考文章:** [http://blog.csdn.net/gugemichael/article/details/9345803](http://blog.csdn.net/gugemichael/article/details/9345803)? 講GC的,S0、S1為什么總有一個是空的 [http://www.fasterj.com/articles/oraclecollectors1.shtml](http://www.fasterj.com/articles/oraclecollectors1.shtml) [http://blog.csdn.net/dc_726/article/details/7934101](http://blog.csdn.net/dc_726/article/details/7934101) [http://jbutton.iteye.com/blog/1569746](http://jbutton.iteye.com/blog/1569746) ### 3、內存性能分析與故障處理工具 | jps | JVM Process Status Tool,顯示指定系統內所有的HotSpot虛擬機進程 | |-----|-----| | -q | 只輸出LVMID,省略主類的名稱 | | -m | 輸出虛擬機進程啟動時傳遞給主類main()函數的參數 | | -l | 輸出主類的全名,如果進程執行的是Jar包,輸出Jar路徑 | | -v | 輸出虛擬機進程啟動時JVM參數 | | jstat | JVM Statistics Monitoring Tool,用于收集HotSpot虛擬機各方面的運行數據 | | -class | 監視類裝載,卸載數量,總空間及類裝載所耗費的時間 | | -gc | 監視Java堆狀況,包括Eden區,2個survivor區,老年代,永久代等的容量,已用空間,GC時間合計等信息 | | -gccapacity | 監視內容與-gc基本相同,但輸出主要關注Java堆各個區域使用到的最大和最小空間 | | -gcutil | 監視內容與-gc基本相同,單輸出主要關注已使用空間占總空間的百分比 | | -gccause | 與-gcutil功能一樣,但是會額外輸出導致上一次GC產生的原因 | | -gcnew | 監視新生代GC的狀況 | | -gcnewcapacity | 監視內容與-gcnew基本相同,輸出主要關注使用到的最大和最小空間 | | -gcold | 監視老年代GC的狀況 | | -gcoldcapacity | 監視內容與-gcold基本相同,輸出主要關注使用到的最大和最小空間 | | -gcpermcapacity | 監視永久代使用到的最大和最小空間 | | -compiler | 輸出JIT編譯器編譯過的方法,耗時等信息 | | -printcompilation | 輸出已經被JIT編譯的方法 | | jinfo | Configuration Info for Java,顯示虛擬機配置信息 | | -flag | jinfo -flag MaxPermSize 進程ID號,顯示參數值 | | -sysprops | 把虛擬機進程的System.getProperties()的內容打印出來 | | jmap | Memory Map for Java,生成虛擬機的內存轉儲快照(heapdump文件) | | -dump | 生成Java堆轉儲快照。格式為:-dump:[live,]format=b,file=<filename>,其中live子參數說明是否只dummp出存活的對象 | | -finalizerinfo | 顯示在F-Queue中等待Finalizer線程執行finalize方法的對象。只在linux/Solaris平臺下有效 | | -heap | 顯示Java堆詳細信息,如使用哪種回收器,參數設置,分代狀況等。只在linux/Solaris平臺下有效 | | -histo | 顯示堆中對象統計信息,包括類、實例數量和合計容量 | | -permstat | 以ClassLoader為統計口徑顯示永久代內存狀態。只在linux/Solaris平臺下有效 | | -F | 當虛擬機進程對-dump選擇沒有響應時,可使用這個選項強制生成dump快照。只在linux/Solaris平臺下有效 | | jhat | JVM Heap Dump Browser,用于分析headdump文件,它會建立一個HTTP/HTML服務器,讓用戶可以在瀏覽器上查看分析結果 | | ? | 實際生產中,沒有人會這么來分析headdump文件 | | jstack | Stack Trace for Java,顯示虛擬機的線程快照 | | -F | 當正常輸出的請求不被響應時,強制輸出線程堆棧 | | -l | 除堆棧外,顯示關于鎖的附加信息 | | -m | 如果調用到本地方法的話,可以顯示C/C++的堆棧 | jstack? -l -F 15348 >> 169.txt? sudo -u root /usr/java/jdk1.6.0_26/bin/jstack -F 15348 在JDK6U23之前,執行jstack,jmap -F會有Bug,看這個文章:[http://developer.51cto.com/art/201203/321359.htm](http://developer.51cto.com/art/201203/321359.htm) **MAT(Memory Analyzer Tool)** 除了以上之外,另外推薦一個內存分析工具,下載地址:http://www.eclipse.org/mat/downloads.php,它分析的是JVM內存的dump文件,此文件可以通過jmap -dump:format=b,file=xxx.bin pid將內存dump出來,也可以通過設置-XX:+HeapDumpOnOutOfMemoryError參數,在JVM出現OOM時自動輸出到文件;或者在VisualVM中直接對監控的JVM進程單擊右鍵,選擇“Heap Dump”,將JVM的內存dump出來。 **BTrace** 下載地址:https://kenai.com/projects/btrace/downloads/directory/releases 寫一段腳本,隨時切入正在運行的程序,跟蹤代碼的調用。寫一段Btrace腳本還是很麻煩的,不詳述了。 其他的工具還有:HSDB,Java自帶的,執行方式如下: java -classpath .:$JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.CLHSDB ### jvisualvm 可以遠程監控Java進程,比如Tomcat,增加啟動參數: JAVA_OPTS="-Dcom.sun.management.jmxremote.port=9998 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=10.10.10.212" 或者java進程,啟動時增加參數,比如: java -Xms2048m -Xmx46080m -classpath $CLASSPATH -Dcom.sun.management.jmxremote.port=9998 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=10.10.10.212 com.yougou.recommend.service.ViewViewServiceReverse >> "$stats_log" 則可以在本地通過jvisualvm監控進程情況,在命令行輸入jvisualvm,遠程,連接ip地址,之后右鍵這個遠程連接,新建JMX連接,輸入端口,則可以監控這個遠程java進程了。 ### 4、調優案例分析與實戰 **高性能硬件上的程序部署策略:** <table border="1" width="100%" cellspacing="1" cellpadding="1"><tbody><tr><td>案例</td><td>場景:在線文檔網站,硬件升級(CPU變為4個,內存變為16G),出現十幾分鐘停頓十幾秒現象<br/>原因:文檔序列化產生大量大對象,進入老年代,導致沒有在Minor GC清理掉,Full GC耗時導致的停頓現象<br/>解決:硬件分成5個虛擬機,做成集群,用前端負載;結果速度比硬件升級前有較大提升,也不再停頓</td></tr><tr><td>兩種方式</td><td>在高性能硬件上部署程序,主要有兩種方式:<br/>1.通過64位JDK來實用大內存<br/>2.實用若干個32位虛擬機建立邏輯集群來利用硬件資源</td></tr><tr><td>結論</td><td>控制Full GC頻率的關鍵是看應用中絕大多數對象能否符合“朝生夕滅”的原則,即大多數對象的生存時間不應當太長,尤其不能產生成批量的、長生存時間的大對象,這樣才能保障老年代空間的穩定;<br/>在大多數網站形式的應用中,主要對象的生存周期都應該是請求級或頁面級的,會話級和全局級的長生命對象相對較少。只要代碼合理,應當都能實現在超大堆中正常使用而沒有Full GC,這樣的話,使用超大堆內存時,網站響應速度才比較有保障。</td></tr></tbody></table> **集群間同步導致的內存溢出:** <table border="1" width="100%" cellspacing="1" cellpadding="1"><tbody><tr><td>案例</td><td>場景:6個節點的WebLogic集群,有數據在各個節點間共享,開始存在數據庫中,后來改為JbossCache全局緩存<br/>原因:既有JBossCache的缺陷,也有實現方式的缺陷,集群間頻繁的寫操作,當網絡情況不能滿足傳輸要求時,重發數據在內存中不斷的堆積,很快就產生了內存溢出</td></tr><tr><td>結論</td><td>對于部分數據在各個節點共享的場景,使用更為成熟的第三方框架,比如Redis</td></tr></tbody></table> **堆外內存導致的溢出錯誤:** <table border="1" width="100%" cellspacing="1" cellpadding="1"><tbody><tr><td>案例</td><td>場景:2G內存,1G堆內存,發送內存溢出,對內存調大為1.6G,溢出反而更頻繁了<br/>解決步驟:利用jstat,發現GC正常,Eden區、Survivor區、老年代、永久代均正常,在內存溢出后從系統日志中找到異常對照,有java.nio.ByteBuffer.allocateDirect<br/>原因:案例使用的CometD1.1.1框架,有大量的NIO操作需要用到DirectMemory</td></tr><tr><td>結論</td><td>Direct Memory:可通過-XX:MaxDirectMemorySize調整大小,內存不足時拋出OutOfMemoryError或OutOfMemoryError:Direct buffer Memory;<br/>線程堆棧:可通過-Xss調整大小,內存不足時拋出StackOverFlowError(縱向無法分配,即無法分配新的棧幀)或OutOfMemoryError:unable to create native thread(橫向無法分配,及無法建立新的線程);<br/>Stock緩存區:每個Socket連接都有Receive和Send兩個緩存區,分別占大約37KB和25KB的內存,連接多的話這塊內存占用也比較可觀。如果無法分配,則可能拋出IOException:Too many open files異常;<br/>JNI代碼:如果代碼中使用JNI調用本地代碼庫,那本地代碼庫使用的內存也不在堆中;<br/>虛擬機和GC:虛擬機和GC的代碼執行也要消耗一定的內存。</td></tr></tbody></table> **外部命令導致系統緩慢:** <table border="1" width="100%" cellspacing="1" cellpadding="1"><tbody><tr><td>案例</td><td>場景:大并發壓力下,請求緩慢,通過監控,發現CPU使用率很高,并且CPU占用不是程序本身,通常情況下,用戶應用的CPU占用應該占主要地位。<br/>解決步驟:通過開發人員找到答案,每個用戶請求,時都要執行一個外部Shell獲得系統的一些信息。這些Shell腳本是通過Java的Runtime.getRuntime().exec()方法調用的。Java虛擬機執行這個命令的過程是:首先克隆一個和當前虛擬機擁有一樣環境變量的進程,再用這個新的進程執行外部命令,最后再退出這個進程。如果頻繁執行這個操作,系統的消耗會很大,不僅是CPU,內存的負擔也很重。<br/>解決:去掉這個shell腳本的執行,改用Java的API去獲取這些信息。</td></tr></tbody></table> **服務器JVM進程崩潰:** <table border="1" width="100%" cellspacing="1" cellpadding="1"><tbody><tr><td>案例</td><td>場景;第二個案例的配置,頻繁出現集群節點的虛擬機進程自動關閉的現象。<br/>找出問題:有異常日志,java.net.SocketException:Connection reset,這是一個遠程斷開連接的異常,通過系統管理員了解到最近和第三方遠程OA做過對接。經過測試,發現調用遠程WebService長達3分鐘才返回,并且返回的結果都是連接中斷。<br/>原因:由于系統經常調用遠程服務,但是兩邊服務的速度完全不對等 ,時間越長就累積越多的服務沒有調用完,導致在等待的線程和Socket連接越來越多,最終超過虛擬機的承受能力后使得虛擬機的進程崩潰。</td></tr><tr><td>結論</td><td>及時驗證接口服務,盡量在允許的情況下,采用異步消息機制,比如RabbitMQ之類。</td></tr></tbody></table> Java虛擬機的內存管理與垃圾回收是虛擬機結構體系中最重要的組成部分,對我們程序的性能和穩定性有著非常大的影響。 # 虛擬機執行系統:類加載、編譯和執行 這一部分深入講解了虛擬機的執行過程,對實際工作中的幫助相對較小,這里只記錄下篇章,及一些領悟,不做深入學習。 篇章:《類文件結構》、《虛擬機類加載機制》、《虛擬機字節碼執行引擎》、《類加載及執行子系統的案例與實戰》、《編譯器優化》、《運行期優化》 這些篇章介紹了Class文件格式、類加載及迅疾執行引擎,這些內容是虛擬機中必不可少的部分,了解了虛擬機如何執行程序,才能更好地理解怎樣才能寫出優秀的代碼。 代碼編譯的結果從本地機器碼變為字節碼,是存儲格式發展的一小步,卻是編程語言發展的一大步。 Java語言規范和Java虛擬機規范是分別定義的,這說明任何語言,編譯成的字節碼只要符合Java虛擬機規范,都可以在Java虛擬機執行,當前這樣的語言有:Clojure,Groovy,JRuby,Jython。 Java程序從源碼編譯成字節碼和從字節碼編譯成本地機器碼的過程,Javac字節碼編譯器與虛擬機內的JIT編譯器的執行過程合并起來,其實就等于一個傳統的編譯器所執行的編譯過程。 對Java編譯器的深入了解,有助于在工作中分辨哪些代碼是編譯器可以幫我們處理的,哪些代碼需要自己調節以便更適合編譯器的優化。 # 高效并發 并發處理的廣泛應用是使得Amdahl定律代替摩爾定律成為計算機性能發展源動力的根本原因,也是人類壓榨計算機運算能力最有力的武器。 volatile修飾符,當一個變量被定義為volatile后,則此變量對多有線程可見,即新值修改了值后,其他線程立即得知;但是這種類型不是原子性的, 依然做不到線程安全,書中有實例。 immutable的 對象一定是線程安全的,比如String對象。 絕對線程安全的對象幾乎沒有,書中演示了Vector對象多線程移除,get時獲取不到的情況,依然需要對vector對象加鎖才保證了正確的結果。Java API中,絕大多數的線程安全類,都是相對線程安全的。 suspend,resume,setIn,setOut,runFinalizersOnExit等有死鎖風險,被廢棄。 線程安全最常用的實現,就是互斥同步,是阻塞的,利用synchronized。 本書的后面部分,關于虛擬機執行系統的,關于編譯優化的,關于高效并發的,和實際編碼、架構等技術性工作的相關性相對較低,在此不進行進一步的深入學習。 # 內存溢出代碼示例 ### HeapSize OOM -- Java heap space ~~~ class HeapOOM { public static void main(String[] args) { java.util.List<String> list = new java.util.ArrayList<String>(); while(true) { list.add("內存溢出啊,內粗溢出啊!"); } } } ~~~ 執行java -Xmx2m HeapOOM,堆內存瞬間溢出,報錯:java.lang.OutOfMemoryError:Java heap space ### HeapSize OOM -- GC over head limit exceeded ~~~ import java.util.*; class GCOverHead { public final static byte[] DEFAULT_BYTES = new byte[12*1024*1024]; public static void main(String[] args) { List<byte[]> temp = new ArrayList<byte[]>(); while(true) { temp.add(new byte[1024*1024]); if(temp.size()>3) { temp.clear(); } } } } ~~~ 執行java -XX:+PrintGCDetails -XX:+UseGCOverheadLimit -Xmn5m -Xmx20m GCOverHead ,據說會報java.lang.OutOfMemoryError:GC over head limit exceeded,意思是不斷在做Full GC,每次GC完后釋放一點點內存,然后一下子就又滿了,不斷重復,當次數達到一定量,并且平均FULL GC時間達到一定比例時,就會報錯; 但是,我的程序一直就無限循環運行,不報錯誤。。。 ### PermGen OOM ~~~ import net.sf.cglib.proxy.Enhancer; class ClassPermGenOOM { public static void main(String[] args) { while(true) { createProxy(ClassPermGenOOM.class); } } public static Object createProxy(Class<?> targetClass) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(targetClass); enhancer.setUseCache(false); enhancer.setCallback(new MethodInterceptor() { public Object intercept(Object object, Method method,Object[] args, MethodProxy methodProxy) throws Throwable { return methodProxy.invokeSuper(object,args); } }); return enhancer.create(); } } ~~~ 執行java -XX:+TraceClassUnloading -XX:PermSize=10m -XX:MaxPermSize=10 ClassPermGenOOM,會報出java.lang.OutOfMemoryError:PermGen space錯我。 ### DirectBuffer OOM ~~~ import java.nio.ByteBuffer; class ByteBufferOOM { public static void main(String[] args) { ByteBuffer.allocateDirect(257*1024*1024); } } ~ ~~~ 執行java -XX:MaxDirectMemorySize=256m ByteBufferOOM,則會報出錯誤java.lang.OutOfMemoryError:Direct bufer memory。 ### StackOverflowError 棧溢出,無限遞歸即可。 死遞歸和死循環不一樣,死循環時,棧空間不會遞增;而死遞歸由于需要記錄退回的路徑,就必須記住遞歸過程中的方法調用過程,以及每個方法運行過程中的本地變量,這個我們稱之為上下文信息,隨著內容的增加,就會占用更多的內存空間,JVM是為了控制它的無限制增長,才做了安全檢測的處理。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看