日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
詳解JVM的內(nèi)存管理機制

我們在深入Java核心系列文章中給大家講過JVM中的棧和局部變量。在做Java開發(fā)的時候常用的JVM內(nèi)存管理有兩種,一種是堆內(nèi)存,一種是棧內(nèi)存。堆內(nèi)存主要用來存儲程序在運行時創(chuàng)建或?qū)嵗膶ο笈c變量,例如:我們通過new MyClass()創(chuàng)建的類MyClass的對象。而棧內(nèi)存則是用來存儲程序代碼中聲明為靜態(tài)(或非靜態(tài))的方法。下面我給大家舉個例子:

成都創(chuàng)新互聯(lián)公司堅持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:網(wǎng)站設(shè)計制作、成都網(wǎng)站設(shè)計、企業(yè)官網(wǎng)、英文網(wǎng)站、手機端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時代的義烏網(wǎng)站設(shè)計、移動媒體設(shè)計的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!

 
 
 
  1. 代碼
  2.  public class Test{
  3.       static Vector list = new Vector();
  4.       static void makeThings(){
  5.            Object object = new Object();
  6.            list.add(object);
  7.        }
  8.        public static void main(){
  9.              makeThings();
  10.        }
  11.  }

就拿上面的例子來說,放在棧內(nèi)存中的有:main,makeThings,放在堆內(nèi)存中有:Test,list,object。

JVM中對象的生命周期大致可以分為7個階段:創(chuàng)建階段、應(yīng)用階段、不可視階段、不可到達階段、可收集階段、終結(jié)階段與釋放階段。

1.創(chuàng)建階段:

(1)為對象分配存儲空間。

(2)開始構(gòu)造對象。

(3)遞歸調(diào)用其超類的構(gòu)造方法。

(4)進行對象實力初始化與變量初始化。

(5)執(zhí)行構(gòu)造方法體。

還有就是你在創(chuàng)建對象的時候需要注意的地方:

(1)避免在循環(huán)體中創(chuàng)建對象,即使該對象占用內(nèi)存空間不大。

(2)盡量及時使對象符合垃圾回收標準。

(3)不要采用過深的繼承層次。

(4)訪問本地變量優(yōu)于訪問類中的變量。

2.應(yīng)用階段:

在應(yīng)用階段涉及到4個引用:

(1)強引用:是指JVM內(nèi)存管理器從根引用集合出發(fā)遍尋堆中所有到達對象的路徑。

(2)軟引用:是具有較強的引用功能,只有當內(nèi)存不夠的時候,才回收這類內(nèi)存,因此內(nèi)存足夠的時候,不會被回收。

(3)弱引用:弱引用與軟引用對象的最大不同在于:GC在進行回收時,需要通過算法檢查是否回收軟引用對象,而對于弱引用來說,GC總是進行回收。

(4)虛引用:主要用于輔助finalize函數(shù)的使用。虛引用主要適用于以某種比Java終結(jié)機制更靈活的方式調(diào)度pre-mortem清除操作。

3.不可視階段:

先看一段代碼:

 
 
 
  1. 代碼
  2.  public void process(){
  3.     try{
  4.          Object obj = new Object();
  5.          obj.doSomething();
  6.     }
  7.      catch(Exception e){
  8.          e.printStackTrace();
  9.     }
  10.      while(isLoop){
  11.         //這個區(qū)域?qū)τ趏bj對象來說已經(jīng)是不可視的了
  12.         //因此下面的代碼在編譯時會引發(fā)錯誤
  13.           obj.doSomething();
  14.     }
  15.  }

如果一個對象已使用完了,應(yīng)該主動將其設(shè)置為null,可以在上面的代碼行obj.doSomething();下添加代碼行obj=null;這樣一行代碼強制將obj對象置為空值,這樣做的意義就是幫助JVM及時的發(fā)現(xiàn)這個垃圾對象,并且可以及時的回收該對象占用的系統(tǒng)資源。

4.不可到達階段:

處于不可到達階段的對象,在虛擬機所管理的對象引用根集合中再也找不到直接或間接的強引用,這些對象通常是指多有線程棧中的臨時變量,所有已裝載的類的靜態(tài)變量或者對本地代碼接口(JNI)引用。

 5.可收集階段、終結(jié)階段與釋放階段:

當對象處于這個階段的時候,可能處于下面三種情況:

(1)垃圾回收器發(fā)現(xiàn)該對象已經(jīng)不可到達。

(2)finalize方法已經(jīng)被執(zhí)行。

(3)對象空間已被重用。

當對象處于上面三種清空的時候,虛擬機就可以直接將該對象回收了。#p#

析構(gòu)方法finalize

前面我們說了JVM的垃圾回收機制和JVM中對象的生命周期,今天給大家講個方法,叫做析構(gòu)方法finalize,我想搞過C++的人都知道,而且是內(nèi)存管理技術(shù)中相當重要的一部分。但是,在Java中好像沒有這個概念,這是因為,理論上JVM負責對象的析構(gòu)(銷毀與回收)工作,finalize是Object類中的一個方法,并且是protected,由于所有的類都繼承了Object對象,因此,就都隱式的繼承了改方法,不過可以重寫這個方法,如果重寫此方法,最后一句必須寫上super.finalize()語句,因為finalize方法沒有自動實現(xiàn)遞歸調(diào)用。那我們在什么時候要重寫它呢?當有一些不容易控制并且非常重要的資源時,要放到finalize方法中,例如:一些I/O的操作,數(shù)據(jù)的連接等等,這些資源的釋放對整個應(yīng)用程序是非常關(guān)鍵的。

我先讓大家看一段代碼:

 
 
 
  1. public class TestA{
  2.     Object obj = null;
  3.     public TestA(){
  4.          obj = new Object();
  5.          System.out.println("創(chuàng)建obj對象");
  6.      }
  7.      protected void destroy(){
  8.          System.out.println("釋放obj對象");
  9.          obj = null;
  10.         //釋放自身所占用的資源
  11.      }
  12.     protected void finalize() throws java.long.Throwable{
  13.         destroy();
  14.         //遞歸調(diào)用超類中的finalize方法
  15.          super.finalize();
  16.      }
  17.  }

finalize方法最終是由JVM中的垃圾回收器調(diào)用的,由于垃圾回收器調(diào)用finalize的時間是不確定或者不及時的,調(diào)用時機對我們來說是不可控的,因此我們可以在自己的類中聲明一個destory()方法,在這個方法中添加釋放系統(tǒng)資源的處理代碼,但是還是建議你將對destroy()方法的調(diào)用放入當前類的finalize()方法體中,因為這樣做更保險,更安全。#p#

靜態(tài)變量

我們知道類中的靜態(tài)變量在程序運行期間,其內(nèi)存空間對所有該類的對象實例而言是共享的,為了節(jié)省系統(tǒng)內(nèi)存開銷、共享資源,應(yīng)該將一些變量聲明為靜態(tài)變量。通過下面的例子,你就會發(fā)現(xiàn)有什么不同。

代碼一:

 
 
 
  1. public class MemoryTest {
  2.      static class Data{
  3.          private int week;
  4.          private String name;
  5.          Data(int i, String s){
  6.              week = i;
  7.              name = s;
  8.          }
  9.      }
  10.      
  11.      Data weeks[] = {
  12.              new Data(1,"monday"),
  13.              new Data(2,"Tuesday"),
  14.              new Data(3,"Wednesday"),
  15.              new Data(4,"Thursday"),
  16.              new Data(5,"Friday"),
  17.              new Data(6,"Saturday"),
  18.              new Data(7,"Sunday")
  19.      };
  20.      
  21.      public static void main(String[] args) {
  22.          final int N = 20000;
  23.          MemoryTest test = null;
  24.          for (int i = 0; i <=N; i++) {
  25.             test = new MemoryTest();
  26.          }
  27.          System.out.println(test.weeks.length);
  28.      }
  29. }

代碼二:

 
 
 
  1. public class MemoryTest {
  2.       static class Data{
  3.           private int week;
  4.           private String name;
  5.           Data(int i, String s){
  6.               week = i;
  7.               name = s;
  8.           }
  9.       }
  10.      
  11.      static Data weeks[] = {
  12.              new Data(1,"monday"),
  13.              new Data(2,"Tuesday"),
  14.              new Data(3,"Wednesday"),
  15.              new Data(4,"Thursday"),
  16.              new Data(5,"Friday"),
  17.              new Data(6,"Saturday"),
  18.              new Data(7,"Sunday")
  19.      };
  20.      
  21.      public static void main(String[] args) {
  22.          final int N = 20000;
  23.          MemoryTest test = null;
  24.          for (int i = 0; i <=N; i++) {
  25.              test = new MemoryTest();
  26.          }
  27.          System.out.println(test.weeks.length);
  28.      }
  29.  }

我想大家應(yīng)該發(fā)現(xiàn)上面那兩個類的區(qū)別了吧!

代碼一會在內(nèi)存中保存20000個weeks的副本,而代碼二則在內(nèi)存中保存1個weeks的副本,然后共享該副本,這樣的話就不會造成內(nèi)存的浪費。

雖然靜態(tài)的變量能節(jié)約大量的內(nèi)存,但是并不是所有的地方都適合用,建議大家在下列條件都符合的情況下,盡量用靜態(tài)變量:

(1)變量所包含的對象體積較大,占用內(nèi)存較多。

(2)變量所包含的對象生命周期較長。

(3)變量所包含的對象數(shù)據(jù)穩(wěn)定。

(4)該類的對象實例有對該變量所包含的對象的共享需求。

如果變量不具備上述特點,建議不要輕易使用靜態(tài)變量,以免弄巧成拙。

最后,再提一點內(nèi)存的優(yōu)化,就是有關(guān)對象的重用,比如:對象池和數(shù)據(jù)庫連接池等。那樣的話,是很節(jié)約內(nèi)存空間的,不過,在用的時候要考慮各個方面,比如:運行環(huán)境的內(nèi)存資源的限制等。為了防止對象池中的對象過多,要記得清除。#p#

內(nèi)存管理有許多技巧和方式

其實內(nèi)存管理有許多技巧和方式,在這,我給大家介紹一下。

(1)要盡早的釋放無用對象的引用。如果,該對象不用了,你可以把它設(shè)置為null。但要注意,如果該對象是某方法的返回值,千萬不要這樣處理,否則你從該方法中得到的返回值永遠為空,而且這種錯誤不易被發(fā)現(xiàn),因此這時很難及時抓住、排除NullPointerException異常。

(2)盡量少用finalize函數(shù)。因為它會加大GC的工作量,因此盡量少用finalize方式回收資源。

(3)如果需要使用經(jīng)常用到的圖片,可以使用soft應(yīng)用類型(也就是轉(zhuǎn)換為軟引用類型),它可以盡可能將圖片保存在內(nèi)存中,供程序調(diào)用,而不引起OutOfMemory。

(4)注意集合數(shù)據(jù)類型,包括數(shù)組、樹、圖、鏈表等數(shù)據(jù)結(jié)構(gòu),這些數(shù)據(jù)結(jié)構(gòu)對于GC來說,回收更為復(fù)雜。另外,要注意那些全局變量,靜態(tài)變量,這些對象往往容易引起懸掛對象,造成內(nèi)存浪費。

(5)盡量避免在類的默認構(gòu)造器中創(chuàng)建、初始化大量的對象,防止在調(diào)用其子類的構(gòu)造器時造成不必要的內(nèi)存資源浪費。

(6)盡量避免強制系統(tǒng)做垃圾內(nèi)存回收(通過顯式調(diào)用方法System.gc()),增長系統(tǒng)做垃圾回收的最終時間,降低系統(tǒng)性能。

(7)盡量避免顯式申請數(shù)組空間,當不得不顯式申請數(shù)組空間時盡量準確的估計出其合理值,以免造成不必要的系統(tǒng)內(nèi)存開銷。

(8)盡量在做遠程方法調(diào)用(RMI)類應(yīng)用開發(fā)時使用瞬間值變量,除非遠程調(diào)用端需要獲取該瞬間值變量的值。

(9)盡量在合適的場景下使用對象池技術(shù)以提高系統(tǒng)的性能,縮減系統(tǒng)內(nèi)存開銷,但是要注意對象池的尺寸不易過大,及時清除無效對象釋放內(nèi)存資源,綜合考慮應(yīng)用運行環(huán)境的內(nèi)存資源限制,避免過高估計運行環(huán)境所提供內(nèi)存資源的數(shù)量。

雖然,這些技巧提高不了多少性能,但是,在嵌入式開發(fā),或者要求性能比較高的系統(tǒng)中卻很有用。


新聞名稱:詳解JVM的內(nèi)存管理機制
網(wǎng)址分享:http://www.dlmjj.cn/article/dpoiihs.html