搜索關鍵字: com.mysql.jdbc.NonRegisteringDriver connectionphantomref
https://yiweifen.com/v-1-279997.html
https://stackoverflow.com/questions/23606041/spring-jdbctemplate-transaction-manager-memory-leak#
https://bugs.mysql.com/bug.php?id=68400
http://www.devguli.com/blog/eng/java-deflater-and-outofmemoryerror/
https://bbs.csdn.net/topics/391875207
https://forums.mysql.com/read.php?39,627417,627417
https://blog.csdn.net/arjick/article/details/41311411
https://blog.csdn.net/visualcatsharp/article/details/26228659
https://blog.csdn.net/zlfing/article/details/80770539
https://blog.csdn.net/developerFBI/article/details/23523727
https://blog.csdn.net/u012102536/article/details/58587090
https://blog.csdn.net/ywb201314/article/details/52037341
概述
HotSpot是一種JVM,是jdk的默認虛擬機。HotSpot是較新的Java虛擬機技術,用來代替JIT(Just in Time)技術,可以大大提高Java運行的性能。
Java原先是把源代碼編譯為字節碼在虛擬機執行,這樣執行速度較慢。而該技術將常用的部分代碼編譯為本地(原生,native)代碼,這樣顯著提高了性能。用於服務器版和標准版的HotSpot有所不同。
內存空間分類
線程棧空間
包括程序計數器、java線程棧和本地線程棧
元數據區Metaspace
Java8中的Metaspace就是以前的永久代,存放了要加載的類信息、靜態變量、final類型的常量、屬性和方法信息。
堆
Java 中的堆是 JVM 所管理的最大的一塊內存空間,主要用於存放各種類的實例對象。在 Java 中,堆被划分成兩個不同的區域:新生代 ( Young )、老年代 ( Old )。 默認的,新生代 ( Young ) 與老年代 ( Old ) 的比例的值為 1:2。
其中,堆的大小可以通過參數 –Xms、-Xmx 來指定。
新生代
新生代 ( Young ) 又被划分為三個區域:Eden、From Survivor、To Survivor。單個區域的空間比例是8:1:1。JVM 每次只會使用 Eden 和其中的一塊 Survivor 區域來為對象服務,所以無論什么時候,總是有一塊 Survivor 區域是空閑着的。 因此,新生代實際可用的內存空間為 9/10 ( 即90% )的新生代空間。
新建對象內存分配規則
1、默認會在Eden和From Survivor上分配空間。
2、當申請的空間大小超過新生代的空閑空間時,會直接將對象創建在老生代。當然也可以顯示指定這個閾值,只要超過這個閾值的對象,都會直接創建在老生代中。
3、當對象所需的空間大小超過了老生代的空閑空間,則直接回拋出OOM:heap的異常。
內存回收
用javavisualVM來查看,能明顯觀察到新生代滿了后,會把對象轉移到舊生代,然后清空繼續裝載,當舊生代也滿了后,就會報outofmemory的異常。
新生代回收:(復制算法)
新生代幾乎是所有 Java 對象出生的地方,即 Java 對象申請的內存以及存放都是在這個地方。Java 中的大部分對象通常不需長久存活,具有朝生夕滅的性質。 當一個對象被判定為 "死亡" 的時候,GC 就有責任來回收掉這部分對象的內存空間。新生代是 GC 收集垃圾的頻繁區域。
在堆中,新生代主要存放的是哪些很快就會被GC回收掉的或者不是特別大的對象(是否設置了-XX:PretenureSizeThreshold 參數)。復制算法的新生代分為3個區:較大的Eden和兩個較小的Survivor(默認的Eden:Survivor = 8:1)。發生在新生代的GC為Minor GC 。在Minor GC時會將新生代中還存活着的對象復制進一個Survivor中,然后對Eden和另一個Survivor進行清理。所以,平常可用的新生代大小為Eden的大小+一個Survivor的大小。
老年代回收:(標記-清除算法/標記-整理算法)
老年代則是存放那些在程序中經歷了好幾次回收仍然還活着或者特別大的對象(是否設置了-XX:PretenureSizeThreshold 參數)。
默認的方式首先是盡量清空新生代 ( YoungGen ),因此在調 System.gc() 時,新生代 ( YoungGen ) 中存活的對象會提前進入老年代
老年代采用的是標記-清除或者標記-整理算法,這兩個算法主要看虛擬機采用的哪個收集器,兩種算法的區別是:標記-清除可能會產生大量連續的內存碎片。在老年代中的GC則為Major GC。Major GC和Full GC會造成stop-the-world。
標記:(一致)遍歷GC Roots,將存活的對象標記
整理:移動所有存活對象,按照內存地址次序依次排列,將末端內存地址以后的內存全部回收
新生代進入老年代的規則
1.分配擔保機制:當Minor GC時,新生代存活的對象大於Survivor的大小時,這時一個Survivor裝不下它們,那么它們就會進入老年代。
2.如果設置了-XX:PretenureSizeThreshold3M 那么大於3M的對象就會直接就進入老年代。
3.在新生代的每一次Minor GC 都會給在新生代中的對象+1歲,默認到15歲時就會從新生代進入老年代,可以通過-XX:MaxTenuringThreshold來設置這個臨界點。
相比較而言,在老年代中的對象比新生代中的對象不易回收許多。
永久代回收:(即方法區回收)
JVM的方法區,也被稱為永久代。在這里都是放着一些被虛擬機加載的類信息,靜態變量,常量等數據。這個區中的東西比老年代和新生代更不容易回收。
JTW
https://www.jianshu.com/p/1f897ab1eed0
在垃圾回收過程中經常涉及到對對象的挪動(比如上文提到的對象在Survivor 0和Survivor 1之間的復制),進而導致需要對對象引用進行更新。為了保證引用更新的正確性,Java將暫停所有其他的線程,這種情況被稱為“Stop-The-World”,導致系統全局停頓。Stop-The-World對系統性能存在影響,因此垃圾回收的一個原則是盡量減少“Stop-The-World”的時間。
不同垃圾收集器的Stop-The-World情況,Serial、Parallel和CMS收集器均存在不同程度的Stop-The-Word情況;而即便是最新的G1收集器也不例外。
-
Java中一種全局暫停的現象,jvm掛起狀態
-
全局停頓,所有Java代碼停止,native代碼可以執行,但不能和JVM交互
-
多半由於jvm的GC引起,如:
1.老年代空間不足。
2.永生代(jkd7)或者元數據空間(jkd8)不足。
3.System.gc()方法調用。
4.CMS GC時出現promotion failed和concurrent mode failure
5.YoungGC時晉升老年代的內存平均值大於老年代剩余空間
6.有連續的大對象需要分配 -
除了GC還有以下原因:
1.Dump線程--人為因素。
2.死鎖檢查。
3.堆Dump--人為因素。
鏈接:https://www.jianshu.com/p/d686e108d15f
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
在虛擬機進行GC的時候會需要讓所用的線程都停止工作,等待他GC完成,那么他是如何保證所有線程全部都停止工作的呢?
只有當所有線程都跑到了安全點,或者進入安全區域之后,才會進行GC
安全點
在安全點,虛擬機會生成OopMap用來記錄引用關系(這也是不能在任何地方停下的原因,如果每一條指令都生成OopMap那么效率會非常低,也會占用大量的空間)
一般安全點設置在以下位置:
方法調用
循環跳轉
異常跳轉
那么JVM是如何讓線程停下的呢?事先會約定一個標志,當需要進行GC的時候,JVM會更改這個標志的值,線程在運行的時候會輪詢這個標志,當收到要發生GC信號,他會運行到下一個安全點停下來,等待GC的進行
當然,僅僅用安全點是不夠的,有下面一種情況,就是當線程sleep或者阻塞的時候,他根本就不會運行,更談不上進入安全點了,更不可能讓所有的線程去等它,於是引入了安全區域這個概念
安全區域
當線程進入安全區域,如sleep或者阻塞時,會標志自己已經進入了安全區域,當進行GC的時候,就不用去管它了,當他要離開安全區域是,會先看看JVM已經完成了GC沒有,如果沒有就等到GC完成之后再離開安全區域
————————————————
版權聲明:本文為CSDN博主「Crazy丶Mark」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_39907763/java/article/details/79423295
內存溢出分類
除了程序計數器,其他區域都有可能會因為可能的空間不足發生 OutOfMemoryError,簡單總結如下:
- 堆內存不足是最常見的 OOM 原因之一,拋出的錯誤信息是“java.lang.OutOfMemoryError:Java heap space”,原因可能千奇百怪,例如,可能存在內存泄漏問題;也很有可能就是堆的大小不合理,比如我們要處理比較可觀的數據量,但是沒有顯式指定 JVM 堆大小或者指定數值偏小;或者出現 JVM 處理引用不及時,導致堆積起來,內存無法釋放等。
- 而對於 Java 虛擬機棧和本地方法棧,這里要稍微復雜一點。如果我們寫一段程序不斷的進行遞歸調用,而且沒有退出條件,就會導致不斷地進行壓棧。類似這種情況,JVM 實際會拋出 StackOverFlowError;當然,如果 JVM 試圖去擴展棧空間的的時候失敗,則會拋出 OutOfMemoryError。
- 對於老版本的 Oracle JDK,因為永久代的大小是有限的,並且 JVM 對永久代垃圾回收(如,常量池回收、卸載不再需要的類型)非常不積極,所以當我們不斷添加新類型的時候,永久代出現 OutOfMemoryError 也非常多見,尤其是在運行時存在大量動態類型生成的場合;類似 Intern 字符串緩存占用太多空間,也會導致 OOM 問題。對應的異常信息,會標記出來和永久代相關:“java.lang.OutOfMemoryError: PermGen space”。
- 隨着元數據區的引入,方法區內存已經不再那么窘迫,所以相應的 OOM 有所改觀,出現 OOM,異常信息則變成了:“java.lang.OutOfMemoryError: Metaspace”。
- 直接內存不足,也會導致 OOM
jvm配置參數
下面只列舉其中的幾個常用和容易掌握的配置選項
| -Xms |
初始堆大小。如:-Xms256m |
| -Xmx |
最大堆大小。如:-Xmx512m |
| -Xmn |
新生代大小。通常為 Xmx 的 1/3 或 1/4。新生代 = Eden + 2 個 Survivor 空間。實際可用空間為 = Eden + 1 個 Survivor,即 90% |
| -Xss |
JDK1.5+ 每個線程堆棧大小為 1M,一般來說如果棧不是很深的話, 1M 是絕對夠用了的。 |
| -XX:NewRatio |
新生代與老年代的比例,如 –XX:NewRatio=2,則新生代占整個堆空間的1/3,老年代占2/3 |
| -XX:SurvivorRatio |
新生代中 Eden 與 Survivor 的比值。默認值為 8。即 Eden 占新生代空間的 8/10,另外兩個 Survivor 各占 1/10 |
| -XX:PermSize |
永久代(方法區)的初始大小 |
| -XX:MaxPermSize |
永久代(方法區)的最大值 |
| -XX:+PrintGCDetails |
打印 GC 信息 |
| -XX:+HeapDumpOnOutOfMemoryError |
讓虛擬機在發生內存溢出時 Dump 出當前的內存堆轉儲快照,以便分析用 |
