激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Java jvm垃圾回收詳解

Java jvm垃圾回收詳解

2022-03-01 00:43月亮的-影子 Java教程

這篇文章主要介紹了JVM的垃圾回收總結,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

常見面試題

  • 如何判斷對象是否死亡
  • 簡單介紹一下強引用、軟引用、弱引用、虛引用
  • 如何判斷常量是一個廢棄常量
  • 如何判斷類是一個無用類
  • 垃圾收集有哪些算法、各自的特點?
  • 常見的垃圾回收器有哪些?
  • 介紹一下CMS,G1收集器?
  • minor gc和full gc有什么不同呢?

Java jvm垃圾回收詳解

 

1.JVM內存回收和分配

1.1主要的區域?

  • 在伊甸區先產生對象
  • 然后發生一次gc之后去到幸存區幸存區
  • 如果年齡大于閾值那么就會升級到老年代

閾值的計算

如果某個年齡段的大小大于幸存區的一半,那么就取閾值或者是這個年齡最小的那個作為新的閾值升級到老年代

  • gc的時候是幸存區的from和伊甸區的存活對象復制到to,然后再清理其它的對象,接著from和to就會交換指針

gc測試

場景就是先給eden分配足量的空間,然后再申請大量空間,問題就是幸存區的空間不夠用

  • 那么這個時候就會觸發分配擔保機制,把多余的對象分配到老年代,而不會觸發full gc。仍然還是monor gc
public class GCTest {
  public static void main(String[] args) {
      byte[] allocation1, allocation2;
      allocation1 = new byte[50900*1024];
      allocation2 = new byte[9500*1024];
  }
}

Java jvm垃圾回收詳解

1.2大對象進入老年代

  • 防止在標記復制的時候占用大量的時間,降低gc的效率

1.3長期存活的對象進入老年代

  • 每次gc都會把eden和from的存活對象放到to,每次gc存活年齡就會+1,如果超過閾值那么就能夠升級到老年代,設置的參數是-XX:MaxTenuringThreshold
  • 下面是計算的方式,每個年齡的人數累加,累加一個就+1,如果對象數量大于幸存區的一半的時候就需要更新閾值(新計算的age和MaxTenuringThreshold)
  • 通常晉升閾值是15,但是CMS是6
uint ageTable::compute_tenuring_threshold(size_t survivor_capacity) {
  //survivor_capacity是survivor空間的大小
  size_t desired_survivor_size = (size_t)((((double)survivor_capacity)*TargetSurvivorRatio)/100);
  size_t total = 0;
  uint age = 1;
  while (age < table_size) {
      //sizes數組是每個年齡段對象大小
      total += sizes[age];
      if (total > desired_survivor_size) {
          break;
      }
      age++;
  }
  uint result = age < MaxTenuringThreshold ? age : MaxTenuringThreshold;
  ...
}

1.4主要進行gc的區域

gc的類型

  • Partial Gc

Young Gc:收集新生代的

Old Gc:只收集老年代的

Mixed Gc:新生代和部分老年代

  • Full Gc:新生代,老年代都會收集

Young Gc

  • 每次都是收集新生代的,并且晉升那些存活久的

Full Gc

  • 如果發現幸存區要晉升的對象內存空間比老年代內存空間更大那么就進行full Gc。有的虛擬機會先進行young gc來清理掉一些,減少full gc的時間消耗

1.5空間分配擔保?

  • jdk1.6之前需要判斷老年代剩余的空間是不是完全大于新生代的空間,如果是那么才能進行minorgc保證不會出現問題。如果是不行就會去檢查-XX:handlePromotionFailure也就是晉升的對象平均大小是不是小于老年代剩余空間,如果是那么就直接minor gc否則就full gc
  • jdk1.6之后直接檢查新生代晉升平均大小如果小于老年代那么就會直接晉升

 

2.對象已經死亡?

Java jvm垃圾回收詳解

2.1引用計數法

  • 其實就是每次被引用那么計數+1,如果計數不是0那么就不會被回收
  • 但是不使用的原因就是循環引用依賴,如果兩個對象互相引用就會導致計數永遠不會為0

2.2可達性分析

  • Gc roots作為起點一直往下面的一條引用鏈

gc Roots的對象

  • 虛擬機棧引用的對象(棧的本地局部變量表)
  • 本地方法棧引用的對象
  • 方法區常量引用的對象(常量池引用的對象)
  • 方法區靜態屬性引用的對象
  • 被同步鎖持有的對象
  • java虛擬機內部引用,比如Integer這些基本類型的

Java jvm垃圾回收詳解

2.3再談引用

  • 強引用:垃圾回收器不會對他進行回收
  • 軟引用:內存空間不足會回收
  • 弱引用:gc就回收
  • 虛引用:隨時會被回收而且需要引用隊列

虛引用、軟引用、弱引用的區別?

  • 虛引用的對象在gc之前會被送到引用隊列,并且程序在對象回收之前做相應的活動(臨死之前的處理)
  • 軟引用是用的最多的,可以提高gc的效率,維護系統安全,防止內存溢出

2.4不可達對象不一定回收

  • 在回收之前會對對象進行一次標記,看是否會執行finalize方法。如果沒有那么這些對象將會先被回收
  • 如果有那么進行第二次標記,讓對象執行finalize之后再進行回收

2.5如何判斷一個常量是廢棄常量?

  • 如果常量池對象沒有被任何對象引用就會被回收
  • jdk1.7之前運行時常量池包含字符串常量池,需要進行復制來返回新的引用(堆有一個,常量池有一個)
  • jdk1.7的時候字符串池已經不在運行時常量池,如果調用intern就會把當前對象放入常量池并且返回引用(只有常量池有一個)。如果本來就存在就會返回對象實例的地址。
  • jdk1.8之后運行時常量池已經轉移到了元空間

2.6如果判斷一個類沒有用?

  • 類的實例都回收了
  • 類的類加載器回收了
  • 類信息沒有被引用
  • 大量的反射和動態代理生成類信息會對方法區產生很大的壓力

 

3.垃圾回收算法

hotspot為什么要區分老年代和新生代?

原因就是不同的存活對象需要不同的垃圾回收算法

  • 如果新生代用的是標記整理,問題就是每次清除大量的對象,移動時間很長,整理消耗很大。但是標記復制就很快,因為存活對象少
  • 但是老年代如果使用標記整理就很好,因為存活多移動少,復制就相反
  • 不能夠統一設計為弱分代假說和強分代假說

跨代收集假說?

如果老年代和新生代互相引用,新生代的年齡就會被拉長。但是為了知道新生代什么時候被gc,這個時候可以給新生代加上一個記憶集(把老年代劃分為很多個格子,代表誰引用了我),避免掃描整個老年代

 

4.垃圾回收器

4.1Serial收集器

  • 單線程收集器,每次都要阻塞其它線程(STW),一個垃圾線程單獨回收
  • 新生代是標記復制,老年代是標記整理
  • 它簡單高效,沒有和其它線程交換不會產生并發問題
  • 但是STW會導致響應很慢

4.2ParNew收集器

  • Serial的多線程版本,但是還是會STW
  • 新生代是標記復制,老年代是標記整理

4.3Parallel Scavenge收集器

  • 新生代是標記復制,老年代是標記整理
  • 和ParNew不同的地方就是它完全關注cpu的利用率,也就是處理任務的吞吐量,而不會管STW到底停多久

4.4SerialOld

  • Serial的老年代版本,1.5以前和Parallel Scavenge一起使用,還有別的用途就是CMS的后備方案

4.5Parallel Old收集器

  • Parallel Scavenge收集器的老年代也是注重吞吐量

4.6CMS收集器

  • 注重最小響應時間
  • 垃圾收集器和用戶線程同時工作
  • 初始標記記錄gc root直接相連的對象
  • 并發標記遍歷整個鏈,但是可以和用戶線程并發運行
  • 重新標記修正那些更新的對象的引用鏈,比并發標記短
  • 并發清除

問題?

內存碎片多對cpu資源敏感

Java jvm垃圾回收詳解

4.7G1收集器

同時滿足響應快處理多的問題

特點

  • 并行和并發,使用多個cpu執行gc線程來縮短stw,而且還能與java線程并發執行
  • 分代收集
  • 空間整合:大部分時候使用標記復制
  • 可預測停頓:響應時間快,可以設置stw時間
  • 分區之間的跨代引用,young這里使用了rset(非收集區指向收集區)記錄,老年代那個區域指向了我,老年代使用了卡表劃分了很多個區域,那么minor gc的時候就不需要遍歷整個其它所有區域去看看當前的區域的對象到底有沒有被引用。

Java jvm垃圾回收詳解

補充字符串池的本質

第一個問題是String a="a"的時候做了什么?

  • 先去找常量池是否存在a如果存在那么就直接返回常量池的引用地址返回,如果不存在那么就創建一個在常量池然后再返回引用地址

第二個問題new String(“a”)發生了什么?

  • 先看看常量池是否存在a,如果不存在創建一個在常量池,而且在堆單獨創建一個a對象返回引用(而不是返回常量池的),相當于就是創建了兩次。
  • 如果第二次創建發現已經存在就直接在堆中創建對象。

第三個問題intern的原理?

  • 看看常量池有沒有這個字符串,沒有就創建并返回常量池對象的地址引用
  • 如果有那么直接返回常量池對象的地址引用

String s1=new String(“a”)

String s2=s1.intern();

很明顯s1不等于s2如果上面的問題都清晰知道。s1引用的是堆,而s2引用的是常量池的

第四個問題

String s3=new String(“1”)+new String(“1”);

String s5=s3.intern();

String s4=“11”

那么地方他們相等嗎?當然是相等的,s3會把1存入常量池,但是不會吧11存入常量池因為,還沒編譯出來。調用了intern之后才會把對象存入常量池,而這個時候存入的對象就是s3指向的那個。所以s4指向的也是s3的。如果是s0="11"的話那就不一樣了,s3.intern只會返回常量池的對象引用地址,而不是s3的,因為s3是不能重復intern 11進去的。jdk1.6的話那么無論怎么樣都是錯的,intern是復制一份,而不是把對象存入常量池(因為字符串常量池在方法區,而jdk1.7它在堆所以可以很好的保存s3的引用)

下面的代碼正確分析應該是三個true,但是在test里面就會先緩存了11導致false, true,false的問題。

@Test
public void test4(){
  String s3 = new String("1") + new String("1");
  String s5 = s3.intern();
  String s4 = "11";
  System.out.println(s5 == s3);
  System.out.println(s5 == s4);
  System.out.println(s3 == s4);
  System.out.println("======================");
  String s6 = new String("go") +new String("od");
  String s7 = s6.intern();
  String s8 = "good";
  System.out.println(s6 == s7);
  System.out.println(s7 == s8);
  System.out.println(s6 == s8);
}

finalize的原理

  • 其實就是對象重寫了finalize,那么第一次gc的時候如果發現有finalize,就會把對象帶到F-Queue上面等待,執行finalize方法進行自救,下面就是一個自救過程,new了一個GCTest對象,這個時候test不引用了,那么正常來說這個GCTest就會被回收,但是它觸發了finalize的方法,最后再次在finalize中使用test引用它所以對象沒有被消除
  • 但是finalize是一個守護線程,防止有的finalize是個循環等待方法阻塞整個隊列,影響回收效率
  • 最后一次標記就是在F-queue里面標記這個對象(如果沒有引用)然后釋放
  • finalize實際上是放到了Finalizer線程上實現。然后然引用隊列指向這個雙向鏈表,一旦遇到gc,那么就會調用ReferenceHandler來處理這些節點的finalize調用,調用之后斷開節點,節點就會被回收了
  • finalize上鎖導致執行很慢
public class GCTest {
  static GCTest test;
  public void isAlive(){
      System.out.println("我還活著");
  }
  @Override
  protected void finalize() throws Throwable {
      System.out.println("我要死了");
      test=this;
  }
  public static void main(String[] args) throws InterruptedException {
     test = new GCTest();
      test=null;
      System.gc();
      Thread.sleep(500);
      if(test!=null){
          test.isAlive();
      }else{
          System.out.println("死了");
      }
      test=null;
      System.gc();
      if(test!=null){
          test.isAlive();
      }else{
          System.out.println("死了");
      }
  }
}

 

總結

本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關注服務器之家的更多內容!

原文鏈接:https://blog.csdn.net/m0_46388866/article/details/120916881

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: av色哟哟| 精品久久久久久久久久久久 | 高清中文字幕在线 | 黄色羞羞视频在线观看 | 国产1区2区在线 | 国产免费观看一区二区三区 | 成人午夜精品久久久久久久蜜臀 | 中韩毛片| 在线播放污 | h久久| 成人三级电影在线 | 中文字幕免费一区 | 一区二区精品视频 | 99成人精品视频 | 色综合视频网 | 国产精品一二三区在线观看 | 久久人添人人爽人人爽人人片av | 国产一区视频在线观看免费 | 欧美一级一区二区三区 | 欧美特黄一级高清免费的香蕉 | 成人午夜淫片a | 黄色片免费看看 | 成人在线精品视频 | 国产高潮好爽受不了了夜色 | 最新日韩一区 | 18视频网站在线观看 | 欧美日韩亚洲不卡 | 一级黄色大片在线观看 | 蜜桃一本色道久久综合亚洲精品冫 | 成人午夜网址 | 欧美国产一区二区三区 | 日韩一级免费毛片 | 欧美日韩亚洲国产 | 美女黄视频在线观看 | 羞羞的视频免费在线观看 | 国产精品区一区二区三区 | 国产精品成人一区二区三区电影毛片 | 国产亚洲精品视频中文字幕 | 日韩黄色免费观看 | 成人免费在线网 | 国产亚洲精品久久久久婷婷瑜伽 |