本章面試題如下:
JVM三大性能調優參數,JVM 幾個重要的參數
JVM調優
JVM內存管理,JVM的常見的垃圾收集器,G1垃圾收集器。GC調優,Minor GC ,Full GC 觸發條件
java內存模型
Java垃圾回收機制
jvm怎樣 判斷一個對象是否可回收,怎樣的對象才能作為GC root
OOM說一下?怎么排查?哪些會導致OOM? OOM出現在什么時候
什么是Full GC?GC? major GC? stop the world
描述JVM中一次full gc過程。
JVM中類加載機制,類加載過程,什么是雙親委派模型?,類加載器有哪些
如何判斷是否有內存泄露?定位 Full GC 發生的原因,有哪些方式?
Java 中都有哪些引用類型?
JVM三大性能調優參數
在這里插入圖片描述
JVM 幾個重要的參數:
-server -Xmx3g -Xms3g -XX:MaxPermSize=128m
-XX:NewRatio=1 新生代(Eden + 2*S)與老年代(不包括永久區)的比值
-XX:SurvivorRatio=8 2個Survivor區和Eden區的比值
-XX:+UseParallelGC
-XX:ParallelGCThreads=8
-XX:+UseParallelOldGC 這個是JAVA 6出現的參數選項
-XX:LargePageSizeInBytes=128m 內存頁的大小, 不可設置過大, 會影響Perm的大小。
-XX:+UseFastAccessorMethods 原始類型的快速優化
-XX:+Disable‘’ExplicitGC 關閉System.gc()
JVM調優
查看堆空間大小分配(年輕代、年老代、持久代分配)
垃圾回收監控(長時間監控回收情況)
線程信息監控:系統線程數量
線程狀態監控:各個線程都處在什么樣的狀態下
線程詳細信息:查看線程內部運行情況,死鎖檢查
CPU熱點:檢查系統哪些方法占用了大量CPU時間
內存熱點:檢查哪些對象在系統中數量最大
jvm問題排查和調優:
jps主要用來輸出JVM中運行的進程狀態信息。
jstat命令可以用于持續觀察虛擬機內存中各個分區的使用率以及GC的統計數據
jmap可以用來查看堆內存的使用詳情。
jstack可以用來查看Java進程內的線程堆棧信息。 jstack是個非常好用的工具,結合應用日志可以迅速定位到問題線程。
Java性能分析工具
jdk會自帶JMC(JavaMissionControl)工具。可以分析本地應用以及連接遠程ip使用。提供了實時分析線程、內存,CPU、GC等信息的可視化界面。
JVM內存調優
對JVM內存的系統級的調優主要的目的是減少GC的頻率和Full GC的次數。 過多的GC和Full GC是會占用很多的系統資源(主要是CPU),影響系統的吞吐量。
使用JDK提供的內存查看工具,比如JConsole和Java VisualVM。
1)監控GC的狀態,使用各種JVM工具,查看當前日志,并且分析當前堆內存快照和gc日志,根據實際的情況看是否需要優化。
2)通過JMX的MBean或者Java的jmap生成當前的Heap信息,并使用Visual VM或者Eclipse自帶的Mat分析dump文件
3)如果參數設置合理,沒有超時日志,GC頻率GC耗時都不高則沒有GC優化的必要,如果GC時間超過1秒或者頻繁GC,則必須優化
4)調整GC類型和內存分配,使用1臺和多臺機器進行測試,進行性能的對比。再做修改,最后通過不斷的試驗和試錯,分析并找到最合適的參數
導致Full GC一般由于以下幾種情況:
舊生代空間不足
調優時盡量讓對象在新生代GC時被回收、讓對象在新生代多存活一段時間和不要創建過大的對象及數組避免直接在舊生代創建對象
新生代設置過小
?一是新生代GC次數非常頻繁,增大系統消耗;二是導致大對象直接進入舊生代,占據了舊生代剩余空間,誘發Full GC
2). 新生代設置過大
,.
一是新生代設置過大會導致舊生代過小(堆總量一定),從而誘發Full GC;二是新生代GC耗時大幅度增加
3). Survivor設置過小
導致對象從eden直接到達舊生代
4). Survivor設置過大
導致eden過小,增加了GC頻率
一般說來新生代占整個堆1/3比較合適
GC策略的設置方式
1). 吞吐量優先 可由-XX:GCTimeRatio=n來設置
2). 暫停時間優先 可由-XX:MaxGCPauseRatio=n來設置
JVM內存管理:
1.JVM內存區域的劃分:
程序計數器(PC,Program Counter Register)。在 JVM 規范中,每個線程都有它自己的程序計數器,并且任何時間一個線程都只有一個方法在執行,也就是所謂的當前方法。程序計數器會存儲當前線程正在執行的 Java 方法的 JVM 指令地址;或者,如果是在執行本地方法,則是未指定值(undefined)。(唯一不會拋出OutOfMemoryError)
第二,Java 虛擬機棧(Java Virtual Machine Stack),早期也叫 Java 棧。每個線程在創建時都會創建一個虛擬機棧,其內部保存一個個的棧幀(Stack Frame),對應著一次次的 Java 方法調用。
前面談程序計數器時,提到了當前方法;同理,在一個時間點,對應的只會有一個活動的棧幀,通常叫作當前幀,方法所在的類叫作當前類。如果在該方法中調用了其他方法,對應的新的棧幀會被創建出來,成為新的當前幀,一直到它返回結果或者執行結束。JVM 直接對 Java 棧的操作只有兩個,就是對棧幀的壓棧和出棧。
棧幀中存儲著局部變量表、操作數(operand)棧、動態鏈接、方法正常退出或者異常退出的定義等。
第三,堆(Heap),它是 Java 內存管理的核心區域,用來放置 Java 對象實例,幾乎所有創建的 Java 對象實例都是被直接分配在堆上。堆被所有的線程共享,在虛擬機啟動時,我們指定的“Xmx”之類參數就是用來指定最大堆空間等指標。
( 編譯器通過逃逸分析,確定對象是在棧上分配還是在堆上分配)
理所當然,堆也是垃圾收集器重點照顧的區域,所以堆內空間還會被不同的垃圾收集器進行進一步的細分,最有名的就是新生代、老年代的劃分。
第四,方法區(Method Area)。這也是所有線程共享的一塊內存區域,用于存儲所謂的元(Meta)數據,例如類結構信息,以及對應的運行時常量池、字段、方法代碼等。
由于早期的 Hotspot JVM 實現,很多人習慣于將方法區稱為永久代(Permanent Generation)。Oracle JDK 8 中將永久代移除,同時增加了元數據區(Metaspace)。
第五,運行時常量池(Run-Time Constant Pool),這是方法區的一部分。如果仔細分析過反編譯的類文件結構,你能看到版本號、字段、方法、超類、接口等各種信息,還有一項信息就是常量池。Java 的常量池可以存放各種常量信息,不管是編譯期生成的各種字面量,還是需要在運行時決定的符號引用,所以它比一般語言的符號表存儲的信息更加寬泛。
第六,本地方法棧(Native Method Stack)。它和 Java 虛擬機棧是非常相似的,支持對本地方法的調用,也是每個線程都會創建一個。在 Oracle Hotspot JVM 中,本地方法棧和 Java 虛擬機棧是在同一塊兒區域,這完全取決于技術實現的決定,并未在規范中強制。
JVM的常見的垃圾收集器:
1.Serial:最古老的垃圾收集器,“Serial”體現在其收集工作是單線程的, 在進行垃圾收集過程中,會進入臭名昭著的“Stop-The-World”狀態,一直是 Client 模式下 JVM 的默認選項。復制算法(-XX:+UseSerialGC)
Serial Old:老年代,采用了標記 - 整理(Mark-Compact)算法
2.ParNew:新生代 GC 實現,是 Serial GC 的多線程版本,最常見的應用場景是配合老年代的 CMS GC 工作(-XX:+UseParNewGC)
3.Parallel Scavenge: 新生代的多線程收集器(并行收集器) 其采用的是Copying算法,是 server 模式 JVM 的默認 GC 選擇(-XX:+UseParallelGC)
4.Parallel Old:并行運行;作用于老年代;標記-整理算法;吞吐量優先;適用于后臺運算而不需要太多交互的場景。
5.CMS:基于標記 - 清除(Mark-Sweep)算法,設計目標是盡量減少停頓時間,采用的標記 - 清除算法,存在著內存碎片化問題,所以難以避免在長時間運行等情況下發生 full GC(-XX:+UseConcMarkSweepGC),在 JDK 9 中被標記為廢棄(deprecated)
6.G1垃圾收集器:
G1 同樣存在著年代的概念,但是與我前面介紹的內存結構很不一樣,其內部是類似棋盤狀的一個個 region 組成。
在這里插入圖片描述
region 的大小是一致的,數值是在 1M 到 32M 字節之間的一個 2 的冪值數,JVM 會盡量劃分 2048 個左右、同等大小的 region。當然這個數字既可以手動調整,G1 也會根據堆大小自動進行調整。在 G1 實現中,年代是個邏輯概念,具體體現在,一部分 region 是作為 Eden,一部分作為 Survivor,除了意料之中的 Old region,G1 會將超過 region 50% 大小的對象(在應用中,通常是 byte 或 char 數組)歸類為 Humongous 對象,并放置在相應的 region 中。邏輯上,Humongous region 算是老年代的一部分,缺點:region 大小和大對象很難保證一致,這會導致空間的浪費。從 GC 算法的角度,G1 選擇的是復合算法,可以簡化理解為:
在新生代,G1 采用的仍然是并行的復制算法,所以同樣會發生 Stop-The-World 的暫停。
在老年代,大部分情況下都是并發標記,而整理(Compact)則是和新生代 GC 時捎帶進行,并且不是整體性的整理,而是增量進行的。習慣上人們喜歡把新生代 GC(Young GC)叫作 Minor GC,老年代 GC 叫作 Major GC,區別于整體性的 Full GC,但是現代 GC 中,這種概念已經不再準確,對于 G1 來說:Minor GC 仍然存在,雖然具體過程會有區別,會涉及 Remembered Set 等相關處理。老年代回收,則是依靠 Mixed GC。并發標記結束后,JVM 就有足夠的信息進行垃圾收集,Mixed GC 不僅同時會清理 Eden、Survivor 區域,而且還會清理部分 Old 區域。有一個重點就是 Remembered Set,用于記錄和維護 region 之間對象的引用關系。G1中提供了兩種模式垃圾回收模式,Young GC和Mixed GC,兩種都是Stop The World(STW)的。
1.YoungGC年輕代收集
在分配一般對象(非巨型對象)時,當所有eden region使用達到最大閥值并且無法申請足夠內存時,會觸發一次YoungGC。每次younggc會回收所有Eden以及Survivor區,并且將存活對象復制到Old區以及另一部分的Survivor區。
2.mixed gc
當越來越多的對象晉升到老年代old region時,為了避免堆內存被耗盡,虛擬機會觸發一個混合的垃圾收集器,即mixed gc,該算法并不是一個old gc,除了回收整個young region,還會回收一部分的old region,這里需要注意:是一部分老年代,而不是全部老年代,可以選擇哪些old region進行收集,從而可以對垃圾回收的耗時時間進行控制。
G1沒有fullGC概念,需要fullGC時,調用serialOldGC進行全堆掃描(包括eden、survivor、o、perm)。
G1收集器的階段分以下幾個步驟:
1、初始標記(它標記了從GC Root開始直接可達的對象)
2、并發標記(從GC Roots開始對堆中對象進行可達性分析,找出存活對象)
3、最終標記(標記那些在并發標記階段發生變化的對象,將被回收)
4、篩選回收(首先對各個Regin的回收價值和成本進行排序,根據用戶所期待的GC停頓時間指定回收計劃,回收一部分Region)
GC調優:
基本的調優思路可以總結為:
1.理解應用需求和問題,確定調優目標。評估用戶可接受的響應時間和業務量,將目標簡化為,希望 GC 暫停盡量控制在 200ms 以內,并且保證一定標準的吞吐量。
2.掌握 JVM 和 GC 的狀態,定位具體的問題,確定真的有 GC 調優的必要。比如,通過 jstat 等工具查看 GC 等相關狀態,可以開啟 GC 日志,或者是利用操作系統提供的診斷工具等
3.選擇的 GC 類型是否符合我們的應用特征,如果是,具體問題表現在哪里,是 Minor GC 過長
4.通過分析確定具體調整的參數或者軟硬件配置。
5.驗證是否達到調優目標,如果達到目標,即可以考慮結束調優;否則,重復完成分析、調整、驗證這個過程。
*** GC日志分析**
調優命令
調優工具
調優命令
Sun JDK監控和故障處理命令有jps jstat jmap jhat jstack jinfo
jps,JVM Process Status Tool,顯示指定系統內所有的HotSpot虛擬機進程。
jstat,JVM statistics Monitoring是用于監視虛擬機運行時狀態信息的命令,它可以顯示出虛擬機進程中的類裝載、內存、垃圾收集、JIT編譯等運行數據。
jmap,JVM Memory Map命令用于生成heap dump文件
jhat,JVM Heap Analysis Tool命令是與jmap搭配使用,用來分析jmap生成的dump,jhat內置了一個微型的HTTP/HTML服務器,生成dump的分析結果后,可以在瀏覽器中查看
jstack,用于生成java虛擬機當前時刻的線程快照。
jinfo,JVM Configuration info 這個命令作用是實時查看和調整虛擬機運行參數。
調優工具
常用調優工具分為兩類,jdk自帶監控工具:jconsole和jvisualvm,第三方有:MAT(Memory Analyzer Tool)、GChisto。
jconsole,Java Monitoring and Management Console是從java5開始,在JDK中自帶的java監控和管理控制臺,用于對JVM中內存,線程和類等的監控
GC觸發的條件有兩種
(1)程序調用System.gc時可以觸發;(2)系統自身來決定GC觸發的時機。
要完全回收一個對象,至少需要經過兩次標記的過程。
第一次標記:對于一個沒有其他引用的對象,篩選該對象是否有必要執行finalize()方法,如果沒有執行必要,則意味可直接回收。(篩選依據:是否復寫或執行過finalize()方法;因為finalize方法只能被執行一次)。
第二次標記:如果被篩選判定位有必要執行,則會放入FQueue隊列,并自動創建一個低優先級的finalize線程來執行釋放操作。如果在一個對象釋放前被其他對象引用,則該對象會被移除FQueue隊列。
Minor GC ,Full GC 觸發條件
Minor GC觸發條件:當Eden區滿時,觸發Minor GC。
Full GC觸發條件:
(1)調用System.gc時,系統建議執行Full GC,但是不必然執行
(2)老年代空間不足
(3)方法區空間不足
(4)通過Minor GC后進入老年代的平均大小大于老年代的可用內存
(5)由Eden區、From Space區向To Sp3ace區復制時,對象大小大于To Space可存,則把該對象轉存到老年代,且老年代的可用內存小于該對象大小
java內存模型
Java內存模型定義了多線程之間共享變量的可見性以及如何在需要的時候對共享變量進行同步。JMM 內部的實現通常是依賴于所謂的內存屏障,通過禁止某些重排序的方式,提供內存可見性保證,也就是實現了各種 happen-before 規則。
與JVM 內存模型不同。
Java內存模型即Java Memory Model,簡稱JMM。JMM定義了Java 虛擬機(JVM)在計算機內存(RAM)中的工作方式。JVM是整個計算機虛擬模型,所以JMM是隸屬于JVM的。
Java內存模型定義了多線程之間共享變量的可見性以及如何在需要的時候對共享變量進行同步。
Java線程之間的通信采用的是過共享內存模型,這里提到的共享內存模型指的就是Java內存模型(簡稱JMM),JMM決定一個線程對共享變量的寫入何時對另一個線程可見。從抽象的角度來看,JMM定義了線程和主內存之間的抽象關系:線程之間的共享變量存儲在主內存(main memory)中,每個線程都有一個私有的本地內存(local memory),本地內存中存儲了該線程以讀/寫共享變量的副本。
java垃圾回收機制
一.如何確定某個對象是“垃圾”?
1)引用計數法。(python)
2) 在Java中采取了 可達性分析法
通過一系列的“GC Roots”對象作為起點進行搜索,如果在“GC Roots”和一個對象之間沒有可達路徑,則稱該對象是不可達的,不過要注意的是被判定為不可達的對象不一定就會成為可回收對象。被判定為不可達的對象要成為可回收對象必須至少經歷兩次標記過程,如果在這兩次標記過程中仍然沒有逃脫成為可回收對象的可能性,則基本上就真的成為可回收對象了。
二.典型的垃圾收集算法
(標記-清除)算法 (復制)算法 (標記-整理)算法 (分代收集)算法
三.典型的垃圾收集器
1.Serial:最古老的垃圾收集器,“Serial”體現在其收集工作是單線程的, 在進行垃圾收集過程中,會進入臭名昭著的“Stop-The-World”狀態,一直是 Client 模式下 JVM 的默認選項。復制算法(-XX:+UseSerialGC)
Serial Old:老年代,采用了標記 - 整理(Mark-Compact)算法
2.ParNew:新生代 GC 實現,是 Serial GC 的多線程版本,最常見的應用場景是配合老年代的 CMS GC 工作(-XX:+UseParNewGC)
3.Parallel Scavenge: 新生代的多線程收集器(并行收集器) 其采用的是Copying算法,是 server 模式 JVM 的默認 GC 選擇(-XX:+UseParallelGC)
4.Parallel Old:并行運行;作用于老年代;標記-整理算法;吞吐量優先;適用于后臺運算而不需要太多交互的場景。
5.CMS:基于標記 - 清除(Mark-Sweep)算法,設計目標是盡量減少停頓時間,采用的標記 - 清除算法,存在著內存碎片化問題,所以難以避免在長時間運行等情況下發生 full GC(-XX:+UseConcMarkSweepGC),在 JDK 9 中被標記為廢棄(deprecated)
6.G1(重點講,引用上面的G1):兼顧吞吐量和停頓時間的 GC 實現,是 Oracle JDK 9 以后的默認 GC 選項。G1 可以直觀的設定停頓時間的目標。
G1 同樣存在著年代的概念,但是與我前面介紹的內存結構很不一樣,其內部是類似棋盤狀的一個個 region 組成。
在這里插入圖片描述
region 的大小是一致的,數值是在 1M 到 32M 字節之間的一個 2 的冪值數,JVM 會盡量劃分 2048 個左右、同等大小的 region。
當然這個數字既可以手動調整,G1 也會根據堆大小自動進行調整。在 G1 實現中,年代是個邏輯概念,具體體現在,一部分 region 是作為 Eden,一部分作為 Survivor,除了意料之中的 Old region,G1 會將超過 region 50% 大小的對象(在應用中,通常是 byte 或 char 數組)歸類為 Humongous 對象,并放置在相應的 region 中。邏輯上,Humongous region 算是老年代的一部分,
缺點:region 大小和大對象很難保證一致,這會導致空間的浪費。
從 GC 算法的角度,G1 選擇的是復合算法,可以簡化理解為:
在新生代,G1 采用的仍然是并行的復制算法,所以同樣會發生 Stop-The-World 的暫停。
在老年代,大部分情況下都是并發標記,而整理(Compact)則是和新生代 GC 時捎帶進行,并且不是整體性的整理,而是增量進行的。
jvm怎樣判斷一個對象是否可回收,怎樣的對象才能作為GC root
1)在Java中采取了 可達性分析法
通過一系列的“GC Roots”對象作為起點進行搜索,如果在“GC Roots”和一個對象之間沒有可達路徑,則稱該對象是不可達的,不過要注意的是被判定為不可達的對象不一定就會成為可回收對象。被判定為不可達的對象要成為可回收對象必須至少經歷兩次標記過程,如果在這兩次標記過程中仍然沒有逃脫成為可回收對象的可能性,則基本上就真的成為可回收對象了。
2)虛擬機棧中引用的對象、方法區類靜態屬性引用的對象、方法區常量池引用的對象、本地方法棧JNI引用的對象
OOM說一下?怎么排查?哪些會導致OOM? OOM出現在什么時候
OOM,全稱“Out Of Memory”,官方說明:當JVM因為沒有足夠的內存來為對象分配空間并且垃圾回收器也已經沒有空間可回收時,就會拋出這個error。
(沒有空閑內存,并且垃圾收集器也無法提供更多內存。)
怎么排查?
首先可以查看服務器運行日志以及項目記錄的日志,捕捉到內存溢出異常。
核心系統日志文件
OOM出現在什么時候?哪些會導致OOM?
java堆內存溢出,此種情況最常見,一般由于內存泄露或者堆的大小設。置不當引起。 可以通過虛擬機參數-Xms,-Xmx等修改。
(1)java永久代溢出,即方法區溢出了,因為永久代的大小是有限的,并且 JVM 對永久代垃圾回收(如,常量池回收、卸載不再需要的類型)非常不積極,所以當我們不斷添加新類型的時候,永久代出現 OutOfMemoryError 也非常多見 ,尤其是在運行時存在大量動態類型生成的場合;(JDK 8 已經沒有方法區了,改為元數據區)
(2)JAVA虛擬機棧溢出,不會拋OOM error,一般是由于程序中存在死循環或者深度遞歸調用造成的,棧大小設置太小也會出現此種溢出。可以通過虛擬機參數-Xss來設置棧的大小。程序不斷的進行遞歸調用,而且沒有退出條件,就會導致不斷地進行壓棧。類似這種情況,JVM 實際會拋出 StackOverFlowError;當然,如果 JVM 試圖去擴展棧空間的的時候失敗,則會拋出 OutOfMemoryError。
(3)直接內存不足,也會導致 OOM
什么是Full GC?GC? major GC? stop the world
從年輕代空間(包括 Eden 和 Survivor 區域)回收內存被稱為 Minor GC。
Minor GC觸發條件:當Eden區滿時,觸發Minor GC。
Full GC 是清理整個堆空間—包括年輕代和老年代。
Full GC觸發條件:
(1)調用System.gc時,系統建議執行Full GC,但是不必然執行
(2)老年代空間不足
(3)方法去空間不足
(4)通過Minor GC后進入老年代的平均大小大于老年代的可用內存
(5)由Eden區、From Space區向To Space區復制時,對象大小大于To Space可用內存,則把該對象轉存到老年代,且老年代的可用內存小于該對象大小
GC,即就是Java垃圾回收機制。GC觸發的條件有兩種。(1)程序調用System.gc時可以觸發;(2)系統自身來決定GC觸發的時機。
兩次標記的過程。
第一次標記:對于一個沒有其他引用的對象,篩選該對象是否有必要執行finalize()方法,如果沒有執行必要,則意味可直接回收。(篩選依據:是否復寫或執行過finalize()方法;因為finalize方法只能被執行一次)。
第二次標記:如果被篩選判定位有必要執行,則會放入FQueue隊列,并自動創建一個低優先級的finalize線程來執行釋放操作。如果在一個對象釋放前被其他對象引用,則該對象會被移除FQueue隊列。
當程序運行到這些“安全點”(方法調用,循環跳轉,異常跳轉)的時候就會暫停所有當前運行的線程(Stop The World 所以叫STW)。
在GC發生時,直接把所有線程都掛起,然后檢測所有線程是否都在安全點,如果不在安全點則恢復線程的執行,等執行到安全點再掛起。
VM會設置一個標志,當線程執行到安全點的時候會輪詢檢測這個標志,如果發現需要GC,則線程會自己掛起,直到GC結束才恢復運行
JVM中類加載機制,類加載過程,什么是雙親委派模型? 類加載器有哪些
一般來說,我們把 Java 的類加載過程分為三個主要步驟:加載、鏈接、初始化,具體行為在Java 虛擬機規范里有非常詳細的定義。
首先是加載階段(Loading),它是 Java 將字節碼數據從不同的數據源讀取到 JVM 中,并映射為 JVM 認可的數據結構(Class 對象),這里的數據源可能是各種各樣的形態,如 jar 文件、class 文件,甚至是網絡數據源等;如果輸入數據不是 ClassFile 的結構,則會拋出 ClassFormatError。
加載階段是用戶參與的階段,我們可以自定義類加載器,去實現自己的類加載過程。
第二階段是鏈接(Linking),這是核心的步驟,簡單說是把原始的類定義信息平滑地轉化入 JVM 運行的過程中。這里可進一步細分為三個步驟:
驗證(Verification),這是虛擬機安全的重要保障,JVM 需要核驗字節信息是符合 Java 虛擬機規范的,否則就被認為是 VerifyError,這樣就防止了惡意信息或者不合規的信息危害 JVM 的運行,驗證階段有可能觸發更多 class 的加載。
準備(Preparation),創建類或接口中的靜態變量,并初始化靜態變量的初始值。但這里的“初始化”和下面的顯式初始化階段是有區別的,側重點在于分配所需要的內存空間,不會去執行更進一步的 JVM 指令。
解析(Resolution),在這一步會將常量池中的符號引用(symbolic reference)替換為直接引用。在Java 虛擬機規范中,詳細介紹了類、接口、方法和字段等各個方面的解析。
最后是初始化階段(initialization),這一步真正去執行類初始化的代碼邏輯,包括靜態字段賦值的動作,以及執行類定義中的靜態初始化塊內的邏輯,編譯器在編譯階段就會把這部分邏輯整理好,父類型的初始化邏輯優先于當前類型的邏輯。
雙親委派模型:
再來談談雙親委派模型,簡單說就是當類加載器(Class-Loader)試圖加載某個類型的時候,除非父加載器找不到相應類型,否則盡量將這個任務代理給當前加載器的父加載器去做。使用委派模型的目的是避免重復加載 Java 類型。
類加載器有哪些:?
1.啟動類加載器:加載 jre/lib 下面的 jar 文件
2.擴展類加載器,負責加載我們放到 jre/lib/ext/ 目錄下面的 jar 包,
3.應用類加載器(Application or App Class-Loader),就是加載我們最熟悉的 classpath 的內容。
如何判斷是否有內存泄露?
泄露可以對比不同時間點內存分配,一般看用戶類型的分配情況,什么在增加。具體,比如用jmap -histo:live 多次快照,然后對比差異,或者用jmc之類profiling工具,都可以進行,對比會更加流暢一些
定位 Full GC 發生的原因,有哪些方式?
1,首先通過printgcdetail 查看fullgc頻率以及時長
2,通過dump 查看內存中哪些對象多,這些可能是引起fullgc的原因,看是否能優化
3,如果堆大或者是生產環境,可以開起jmc 飛行一段時間,查看這期間的相關數據來訂位問題
Java 中都有哪些引用類型?
強引用:發生 gc 的時候不會被回收。 new
軟引用:有用但不是必須的對象,在發生內存溢出之前會被回收。SoftReference
弱引用:有用但不是必須的對象,在下一次GC時會被回收。WeakReference
虛引用(幽靈引用/幻影引用):無法通過虛引用獲得對象,用 PhantomReference 實現虛引用,虛引用的用途是在 gc 時返回一個通知。
PhantomReference pr = new PhantomReference (object, queue);