乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      java線程安全總結(jié)

       燮羽 2010-12-14

      最近想將java基礎的一些東西都整理整理,寫下來,這是對知識的總結(jié),也是一種樂趣。已經(jīng)擬好了提綱,大概分為這幾個主題: java線程安全,java垃圾收集,java并發(fā)包詳細介紹,java profile和jvm性能調(diào)優(yōu) 。慢慢寫吧。本人jameswxx原創(chuàng)文章,轉(zhuǎn)載請注明出處,我費了很多心血,多謝了。關于java線程安全,網(wǎng)上有很多資料,我只想從自己的角度總結(jié)對這方面的考慮,有時候?qū)憱|西是很痛苦的,知道一些東西,但想用文字說清楚,卻不是那么容易。我認為要認識java線程安全,必須了解兩個主要的點:java的內(nèi)存模型,java的線程同步機制。特別是內(nèi)存模型,java的線程同步機制很大程度上都是基于內(nèi)存模型而設定的。后面我還會寫java并發(fā)包的文章,詳細總結(jié)如何利用java并發(fā)包編寫高效安全的多線程并發(fā)程序。暫時寫得比較倉促,后面會慢慢補充完善。


      淺談java內(nèi)存模型 
             不同的平臺,內(nèi)存模型是不一樣的,但是jvm的內(nèi)存模型規(guī)范是統(tǒng)一的。其實java的多線程并發(fā)問題最終都會反映在java的內(nèi)存模型上,所謂線程安全無非是要控制多個線程對某個資源的有序訪問或修改??偨Y(jié)java的內(nèi)存模型,要解決兩個主要的問題:可見性和有序性。我們都知道計算機有高速緩存的存在,處理器并不是每次處理數(shù)據(jù)都是取內(nèi)存的。JVM定義了自己的內(nèi)存模型,屏蔽了底層平臺內(nèi)存管理細節(jié),對于java開發(fā)人員,要清楚在jvm內(nèi)存模型的基礎上,如果解決多線程的可見性和有序性。
             那么,何謂可見性? 多個線程之間是不能互相傳遞數(shù)據(jù)通信的,它們之間的溝通只能通過共享變量來進行。Java內(nèi)存模型(JMM)規(guī)定了jvm有主內(nèi)存,主內(nèi)存是多個線程共享的。當new一個對象的時候,也是被分配在主內(nèi)存中,每個線程都有自己的工作內(nèi)存,工作內(nèi)存存儲了主存的某些對象的副本,當然線程的工作內(nèi)存大小是有限制的。當線程操作某個對象時,執(zhí)行順序如下:
       (1) 從主存復制變量到當前工作內(nèi)存 (read and load)
       (2) 執(zhí)行代碼,改變共享變量值 (use and assign)
       (3) 用工作內(nèi)存數(shù)據(jù)刷新主存相關內(nèi)容 (store and write)

      JVM規(guī)范定義了線程對主存的操作指令:read,load,use,assign,store,write。當一個共享變量在多個線程的工作內(nèi)存中都有副本時,如果一個線程修改了這個共享變量,那么其他線程應該能夠看到這個被修改后的值,這就是多線程的可見性問題。
              那么,什么是有序性呢 ?線程在引用變量時不能直接從主內(nèi)存中引用,如果線程工作內(nèi)存中沒有該變量,則會從主內(nèi)存中拷貝一個副本到工作內(nèi)存中,這個過程為read-load,完成后線程會引用該副本。當同一線程再度引用該字段時,有可能重新從主存中獲取變量副本(read-load-use),也有可能直接引用原來的副本(use),也就是說 read,load,use順序可以由JVM實現(xiàn)系統(tǒng)決定。
              線程不能直接為主存中中字段賦值,它會將值指定給工作內(nèi)存中的變量副本(assign),完成后這個變量副本會同步到主存儲區(qū)(store-write),至于何時同步過去,根據(jù)JVM實現(xiàn)系統(tǒng)決定.有該字段,則會從主內(nèi)存中將該字段賦值到工作內(nèi)存中,這個過程為read-load,完成后線程會引用該變量副本,當同一線程多次重復對字段賦值時,比如:

      Java代碼 
      1. for(int i=0;i<10;i++)  
      2.  a++;  
       


      線程有可能只對工作內(nèi)存中的副本進行賦值,只到最后一次賦值后才同步到主存儲區(qū),所以assign,store,weite順序可以由JVM實現(xiàn)系統(tǒng)決定。假設有一個共享變量x,線程a執(zhí)行x=x+1。從上面的描述中可以知道x=x+1并不是一個原子操作,它的執(zhí)行過程如下:
      1 從主存中讀取變量x副本到工作內(nèi)存
      2 給x加1
      3 將x加1后的值寫回主
       
      如果另外一個線程b執(zhí)行x=x-1,執(zhí)行過程如下:
      1 從主存中讀取變量x副本到工作內(nèi)存
      2 給x減1
      3 將x減1后的值寫回主存
       
      那么顯然,最終的x的值是不可靠的。假設x現(xiàn)在為10,線程a加1,線程b減1,從表面上看,似乎最終x還是為10,但是多線程情況下會有這種情況發(fā)生:
      1:線程a從主存讀取x副本到工作內(nèi)存,工作內(nèi)存中x值為10
      2:線程b從主存讀取x副本到工作內(nèi)存,工作內(nèi)存中x值為10
      3:線程a將工作內(nèi)存中x加1,工作內(nèi)存中x值為11
      4:線程a將x提交主存中,主存中x為11
      5:線程b將工作內(nèi)存中x值減1,工作內(nèi)存中x值為9
      6:線程b將x提交到中主存中,主存中x為9
       
      同樣,x有可能為11,如果x是一個銀行賬戶,線程a存款,線程b扣款,顯然這樣是有嚴重問題的,要解決這個問題,必須保證線程a和線程b是有序執(zhí)行的,并且每個線程執(zhí)行的加1或減1是一個原子操作。看看下面代碼:

      Java代碼 
      1. public class Account {  
      2.   
      3.     private int balance;  
      4.   
      5.     public Account(int balance) {  
      6.         this.balance = balance;  
      7.     }  
      8.   
      9.     public int getBalance() {  
      10.         return balance;  
      11.     }  
      12.   
      13.     public void add(int num) {  
      14.         balance = balance + num;  
      15.     }  
      16.   
      17.     public void withdraw(int num) {  
      18.         balance = balance - num;  
      19.     }  
      20.   
      21.     public static void main(String[] args) throws InterruptedException {  
      22.         Account account = new Account(1000);  
      23.         Thread a = new Thread(new AddThread(account, 20), "add");  
      24.         Thread b = new Thread(new WithdrawThread(account, 20), "withdraw");  
      25.         a.start();  
      26.         b.start();  
      27.         a.join();  
      28.         b.join();  
      29.         System.out.println(account.getBalance());  
      30.     }  
      31.   
      32.     static class AddThread implements Runnable {  
      33.         Account account;  
      34.         int     amount;  
      35.   
      36.         public AddThread(Account account, int amount) {  
      37.             this.account = account;  
      38.             this.amount = amount;  
      39.         }  
      40.   
      41.         public void run() {  
      42.             for (int i = 0; i < 200000; i++) {  
      43.                 account.add(amount);  
      44.             }  
      45.         }  
      46.     }  
      47.   
      48.     static class WithdrawThread implements Runnable {  
      49.         Account account;  
      50.         int     amount;  
      51.   
      52.         public WithdrawThread(Account account, int amount) {  
      53.             this.account = account;  
      54.             this.amount = amount;  
      55.         }  
      56.   
      57.         public void run() {  
      58.             for (int i = 0; i < 100000; i++) {  
      59.                 account.withdraw(amount);  
      60.             }  
      61.         }  
      62.     }  
      63. }  
       


      第一次執(zhí)行結(jié)果為10200,第二次執(zhí)行結(jié)果為1060,每次執(zhí)行的結(jié)果都是不確定的,因為線程的執(zhí)行順序是不可預見的。這是java同步產(chǎn)生的根源,synchronized關鍵字保證了多個線程對于同步塊是互斥的,synchronized作為一種同步手段,解決java多線程的執(zhí)行有序性和內(nèi)存可見性,而volatile關鍵字之解決多線程的內(nèi)存可見性問題。后面將會詳細介紹。



      synchronized關鍵字 
              上面說了,java用synchronized關鍵字做為多線程并發(fā)環(huán)境的執(zhí)行有序性的保證手段之一。當一段代碼會修改共享變量,這一段代碼成為互斥區(qū)或臨界區(qū),為了保證共享變量的正確性,synchronized標示了臨界區(qū)。典型的用法如下:

      Java代碼 
      1. synchronized(鎖){  
      2.      臨界區(qū)代碼  
      3. }   
       


      為了保證銀行賬戶的安全,可以操作賬戶的方法如下:

      Java代碼 
      1. public synchronized void add(int num) {  
      2.      balance = balance + num;  
      3. }  
      4. public synchronized void withdraw(int num) {  
      5.      balance = balance - num;  
      6. }  
       


      剛才不是說了synchronized的用法是這樣的嗎:

      Java代碼 
      1. synchronized(鎖){  
      2. 臨界區(qū)代碼  
      3. }  
       


      那么對于public synchronized void add(int num)這種情況,意味著什么呢?其實這種情況,鎖就是這個方法所在的對象。同理,如果方法是public  static synchronized void add(int num),那么鎖就是這個方法所在的class。
              理論上,每個對象都可以做為鎖,但一個對象做為鎖時,應該被多個線程共享,這樣才顯得有意義,在并發(fā)環(huán)境下,一個沒有共享的對象作為鎖是沒有意義的。假如有這樣的代碼:

      Java代碼 
      1. public class ThreadTest{  
      2.   public void test(){  
      3.      Object lock=new Object();  
      4.      synchronized (lock){  
      5.         //do something  
      6.      }  
      7.   }  
      8. }  
       


      lock變量作為一個鎖存在根本沒有意義,因為它根本不是共享對象,每個線程進來都會執(zhí)行Object lock=new Object();每個線程都有自己的lock,根本不存在鎖競爭。
              每個鎖對象都有兩個隊列,一個是就緒隊列,一個是阻塞隊列,就緒隊列存儲了將要獲得鎖的線程,阻塞隊列存儲了被阻塞的線程,當一個被線程被喚醒(notify)后,才會進入到就緒隊列,等待cpu的調(diào)度。當一開始線程a第一次執(zhí)行account.add方法時,jvm會檢查鎖對象account的就緒隊列是否已經(jīng)有線程在等待,如果有則表明account的鎖已經(jīng)被占用了,由于是第一次運行,account的就緒隊列為空,所以線程a獲得了鎖,執(zhí)行account.add方法。如果恰好在這個時候,線程b要執(zhí)行account.withdraw方法,因為線程a已經(jīng)獲得了鎖還沒有釋放,所以線程b要進入account的就緒隊列,等到得到鎖后才可以執(zhí)行。
      一個線程執(zhí)行臨界區(qū)代碼過程如下:
      1 獲得同步鎖
      2 清空工作內(nèi)存
      3 從主存拷貝變量副本到工作內(nèi)存
      4 對這些變量計算
      5 將變量從工作內(nèi)存寫回到主存
      6 釋放鎖
      可見,synchronized既保證了多線程的并發(fā)有序性,又保證了多線程的內(nèi)存可見性。


      生產(chǎn)者/消費者模式 
              生產(chǎn)者/消費者模式其實是一種很經(jīng)典的線程同步模型,很多時候,并不是光保證多個線程對某共享資源操作的互斥性就夠了,往往多個線程之間都是有協(xié)作的。
              假設有這樣一種情況,有一個桌子,桌子上面有一個盤子,盤子里只能放一顆雞蛋,A專門往盤子里放雞蛋,如果盤子里有雞蛋,則一直等到盤子里沒雞蛋,B專門從盤子里拿雞蛋,如果盤子里沒雞蛋,則等待直到盤子里有雞蛋。其實盤子就是一個互斥區(qū),每次往盤子放雞蛋應該都是互斥的,A的等待其實就是主動放棄鎖,B等待時還要提醒A放雞蛋。
      如何讓線程主動釋放鎖
      很簡單,調(diào)用鎖的wait()方法就好。wait方法是從Object來的,所以任意對象都有這個方法。看這個代碼片段:

      Java代碼 
      1. Object lock=new Object();//聲明了一個對象作為鎖  
      2.    synchronized (lock) {  
      3.        balance = balance - num;  
      4.        //這里放棄了同步鎖,好不容易得到,又放棄了  
      5.        lock.wait();  
      6. }  
       


      如果一個線程獲得了鎖lock,進入了同步塊,執(zhí)行l(wèi)ock.wait(),那么這個線程會進入到lock的阻塞隊列。如果調(diào)用lock.notify()則會通知阻塞隊列的某個線程進入就緒隊列。
      聲明一個盤子,只能放一個雞蛋

       
      Java代碼 
      1. import java.util.ArrayList;  
      2. import java.util.List;  
      3.   
      4. public class Plate {  
      5.   
      6.     List<Object> eggs = new ArrayList<Object>();  
      7.   
      8.     public synchronized Object getEgg() {  
      9.         if (eggs.size() == 0) {  
      10.             try {  
      11.                 wait();  
      12.             } catch (InterruptedException e) {  
      13.             }  
      14.         }  
      15.   
      16.         Object egg = eggs.get(0);  
      17.         eggs.clear();// 清空盤子  
      18.         notify();// 喚醒阻塞隊列的某線程到就緒隊列  
      19.         System.out.println("拿到雞蛋");  
      20.         return egg;  
      21.     }  
      22.   
      23.     public synchronized void putEgg(Object egg) {  
      24.         if (eggs.size() > 0) {  
      25.             try {  
      26.                 wait();  
      27.             } catch (InterruptedException e) {  
      28.             }  
      29.         }  
      30.         eggs.add(egg);// 往盤子里放雞蛋  
      31.         notify();// 喚醒阻塞隊列的某線程到就緒隊列  
      32.         System.out.println("放入雞蛋");  
      33.     }  
      34.       
      35.     static class AddThread extends Thread{  
      36.         private Plate plate;  
      37.         private Object egg=new Object();  
      38.         public AddThread(Plate plate){  
      39.             this.plate=plate;  
      40.         }  
      41.           
      42.         public void run(){  
      43.             for(int i=0;i<5;i++){  
      44.                 plate.putEgg(egg);  
      45.             }  
      46.         }  
      47.     }  
      48.       
      49.     static class GetThread extends Thread{  
      50.         private Plate plate;  
      51.         public GetThread(Plate plate){  
      52.             this.plate=plate;  
      53.         }  
      54.           
      55.         public void run(){  
      56.             for(int i=0;i<5;i++){  
      57.                 plate.getEgg();  
      58.             }  
      59.         }  
      60.     }  
      61.       
      62.     public static void main(String args[]){  
      63.         try {  
      64.             Plate plate=new Plate();  
      65.             Thread add=new Thread(new AddThread(plate));  
      66.             Thread get=new Thread(new GetThread(plate));  
      67.             add.start();  
      68.             get.start();  
      69.             add.join();  
      70.             get.join();  
      71.         } catch (InterruptedException e) {  
      72.             e.printStackTrace();  
      73.         }  
      74.         System.out.println("測試結(jié)束");  
      75.     }  
      76. }  

        執(zhí)行結(jié)果:

      Html代碼 
      1. 放入雞蛋  
      2. 拿到雞蛋  
      3. 放入雞蛋  
      4. 拿到雞蛋  
      5. 放入雞蛋  
      6. 拿到雞蛋  
      7. 放入雞蛋  
      8. 拿到雞蛋  
      9. 放入雞蛋  
      10. 拿到雞蛋  
      11. 測試結(jié)束  
       



      聲明一個Plate對象為plate,被線程A和線程B共享,A專門放雞蛋,B專門拿雞蛋。假設
      1 開始,A調(diào)用plate.putEgg方法,此時eggs.size()為0,因此順利將雞蛋放到盤子,還執(zhí)行了notify()方法,喚醒鎖的阻塞隊列的線程,此時阻塞隊列還沒有線程。
      2 又有一個A線程對象調(diào)用plate.putEgg方法,此時eggs.size()不為0,調(diào)用wait()方法,自己進入了鎖對象的阻塞隊列。
      3 此時,來了一個B線程對象,調(diào)用plate.getEgg方法,eggs.size()不為0,順利的拿到了一個雞蛋,還執(zhí)行了notify()方法,喚醒鎖的阻塞隊列的線程,此時阻塞隊列有一個A線程對象,喚醒后,它進入到就緒隊列,就緒隊列也就它一個,因此馬上得到鎖,開始往盤子里放雞蛋,此時盤子是空的,因此放雞蛋成功。
      4 假設接著來了線程A,就重復2;假設來料線程B,就重復3。
       
      整個過程都保證了放雞蛋,拿雞蛋,放雞蛋,拿雞蛋。



      volatile關鍵字 
             volatile是java提供的一種同步手段,只不過它是輕量級的同步,為什么這么說,因為volatile只能保證多線程的內(nèi)存可見性,不能保證多線程的執(zhí)行有序性。而最徹底的同步要保證有序性和可見性,例如synchronized。任何被volatile修飾的變量,都不拷貝副本到工作內(nèi)存,任何修改都及時寫在主存。因此對于Valatile修飾的變量的修改,所有線程馬上就能看到,但是volatile不能保證對變量的修改是有序的。什么意思呢?假如有這樣的代碼:

      Java代碼 
      1. public class VolatileTest{  
      2.   public volatile int a;  
      3.   public void add(int count){  
      4.        a=a+count;  
      5.   }  
      6. }  
       


              當一個VolatileTest對象被多個線程共享,a的值不一定是正確的,因為a=a+count包含了好幾步操作,而此時多個線程的執(zhí)行是無序的,因為沒有任何機制來保證多個線程的執(zhí)行有序性和原子性。volatile存在的意義是,任何線程對a的修改,都會馬上被其他線程讀取到,因為直接操作主存,沒有線程對工作內(nèi)存和主存的同步。所以,volatile的使用場景是有限的,在有限的一些情形下可以使用 volatile 變量替代鎖。要使 volatile 變量提供理想的線程安全,必須同時滿足下面兩個條件:
      1)對變量的寫操作不依賴于當前值。
      2)該變量沒有包含在具有其他變量的不變式中
       
      volatile只保證了可見性,所以Volatile適合直接賦值的場景,如

      Java代碼 
      1. public class VolatileTest{  
      2.   public volatile int a;  
      3.   public void setA(int a){  
      4.       this.a=a;  
      5.   }  
      6. }  
       

      在沒有volatile聲明時,多線程環(huán)境下,a的最終值不一定是正確的,因為this.a=a;涉及到給a賦值和將a同步回主存的步驟,這個順序可能被打亂。如果用volatile聲明了,讀取主存副本到工作內(nèi)存和同步a到主存的步驟,相當于是一個原子操作。所以簡單來說,volatile適合這種場景:一個變量被多個線程共享,線程直接給這個變量賦值。這是一種很簡單的同步場景,這時候使用volatile的開銷將會非常小。
       

      可能 很多人都覺得莫名其妙,說JVM的內(nèi)存模型,怎么會扯到cpu上去呢?在此,我認為很有必要闡述下,免 得很多人看得不明不白的。先拋開java虛擬機不談,我們都知道,現(xiàn)在的計算機,cpu在計算的時候,并不總是從內(nèi)存讀取數(shù)據(jù),它的數(shù)據(jù)讀取順序優(yōu)先級 是:寄存器-高速緩存-內(nèi)存。線程耗費的是CPU,線程計算的時候,原始的數(shù)據(jù)來自內(nèi)存,在計算過程中,有些數(shù)據(jù)可能被頻繁讀取,這些數(shù)據(jù)被存儲在寄存器 和高速緩存中,當線程計算完后,這些緩存的數(shù)據(jù)在適當?shù)臅r候應該寫回內(nèi)存。當個多個線程同時讀寫某個內(nèi)存數(shù)據(jù)時,就會產(chǎn)生多線程并發(fā)問題,涉及到三個特 性:原子性,有序性,可見性。在《線程安全總結(jié)》這篇文章中,為了理解方便,我把原子性和有序性統(tǒng)一叫做“多線程執(zhí)行有序性”。支持多線程的平臺都會面臨 這種問題,運行在多線程平臺上支持多線程的語言應該提供解決該問題的方案。

       

             那么,我們看看JVM,JVM是一個虛擬 的計算機,它也會面臨多線程并發(fā)問題,java程序運行在java虛擬機平臺上,java程序員不可能直接去控制底層線程對寄存器高速緩存內(nèi)存之間的同 步,那么java從語法層面,應該給開發(fā)人員提供一種解決方案,這個方案就是諸如 synchronized, volatile,鎖機制(如同步塊,就緒隊 列,阻塞隊列)等等。這些方案只是語法層面的,但我們要從本質(zhì)上去理解它,不能僅僅知道一個 synchronized 可以保證同步就完了。   在這里我說的是jvm的內(nèi)存模型,是動態(tài)的,面向多線程并發(fā)的,沿襲JSL的“working memory”的說法,只是不想牽扯到太多底層細節(jié),因為 《線程安全總結(jié)》這篇文章意在說明怎樣從語法層面去理解java的線程同步,知道各個關鍵字的使用場 景。

       

            今天有人問我,那 java的線程不是有棧嗎?難道棧不是工作內(nèi)存嗎?工作內(nèi)存這四個字得放到具體的場景 中描述,方能體現(xiàn)它具體的意義,在描述JVM的線程同步時,工作內(nèi)存指的是寄存器和告訴緩存的抽象描述,具體請自行參閱JLS。上面講的都是動態(tài)的內(nèi)存模 型,甚至已經(jīng)超越了JVM的范圍,那么JVM的內(nèi)存靜態(tài)存儲是怎么劃分的?今天還有人問我,jvm的內(nèi)存模型不是有eden區(qū)嗎?也不見你提起。我跟他 說,這是兩個角度去看的,甚至是兩個不同的范圍,動態(tài)的線程同步的內(nèi)存模型,涵蓋了cpu,寄存器,高速緩存,內(nèi)存;JVM的靜態(tài)內(nèi)存儲模型只是一種對內(nèi) 存的物理劃分而已,它只局限在內(nèi)存,而且只局限在JVM的內(nèi)存。那些什么線程棧,eden區(qū)都僅僅在JVM內(nèi)存。

       

            說說JVM的線程棧和有個朋友反復跟我糾結(jié)的eden區(qū)吧。JVM的內(nèi)存,被劃分了很多的區(qū)域:

      1.程序計數(shù)器 
      每一個Java線程都有一個程序計數(shù)器來用于保存程序執(zhí)行到當前方法的哪一個指令。 
      2.線 程棧 
      線程的每個方法被執(zhí)行的時候,都會同時創(chuàng)建一個幀(Frame)用 于存儲本地變量表、操作棧、動態(tài)鏈接、方法出入口等信息。每一個方法的調(diào)用至完成,就意味著一個幀在VM棧中的入棧至出棧的過程。如果線程請求的棧深度大 于虛擬機所允許的深度,將拋出StackOverflowError異常;如果VM??梢詣討B(tài)擴展(VM Spec中允許固定長度的VM棧),當擴展時無法申請到足夠內(nèi)存則拋出OutOfMemoryError異常。 
      3.本地方法 棧
      4.堆
       
      每個線程的棧都是該線程私有的,堆則是所有線程共享 的。當我們new一個對象時,該對象就被分配到了堆中。但是堆,并不是一個簡單的概念,堆區(qū)又劃分了很多區(qū)域,為什么堆劃分成這么多區(qū)域,這是為了JVM 的內(nèi)存垃圾收集,似乎越扯越遠了,扯到垃圾收集了,現(xiàn)在的jvm的gc都是按代收集,堆區(qū)大致被分為三大塊:新生代,舊生代,持久代(虛擬的);新生代又 分為eden區(qū),s0區(qū),s1區(qū)。新建一個對象時,基本小的對象,生命周期短的對象都會放在新生代的eden區(qū)中,eden區(qū)滿時,有一個小范圍的 gc(minor gc),整個新生代滿時,會有一個大范圍的gc(major gc),將新生代里的部分對象轉(zhuǎn)到舊生代里。 
      5. 方法區(qū) 
      其實就是永久代(Permanent Generation),方法區(qū)中存放了每個Class的結(jié)構(gòu)信息,包括常量池、字段描述、方法描述等等。VM Space描述中對這個區(qū)域的限制非常寬松,除了和Java堆一樣不需要連續(xù)的內(nèi)存,也可以選擇固定大小或者可擴展外,甚至可以選擇不實現(xiàn)垃圾收集。相對 來說,垃圾收集行為在這個區(qū)域是相對比較少發(fā)生的,但并不是某些描述那樣永久代不會發(fā)生GC(至 少對當前主流的商業(yè)JVM實現(xiàn)來說是如此),這里的GC主要是對常量池的回收和對類的卸載,雖然回收的“成績”一般也比較差強人意,尤其是類卸載,條件相 當苛刻。 
      6.常量池 
       Class文件中除 了有類的版本、字段、方法、接口等描述等信息外,還有一項信息是常量表(constant_pool table),用于存放編譯期已可知的常量,這部分內(nèi)容將在類加載后進入方法區(qū)(永久代)存放。但是Java語言并不要求常量一定只有編譯期預置入 Class的常量表的內(nèi)容才能進入方法區(qū)常量池,運行期間也可將新內(nèi)容放入常量池(最典型的String.intern()方法)。

       

      關于垃圾收集,在此不多說,流到垃圾收集那一章再詳細說吧。關于java的同步,其實還有基于 CPU原語的比較并交換的非阻塞算法(CAS),不過這個在java的并發(fā)包里已經(jīng)實現(xiàn)了很多,因此關于這點,就留到java并發(fā)包那一章介紹吧。后面我 會專門寫一篇文章,JVM內(nèi)存與垃圾收集。

        本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多