jvm 內存溢出問題排查方法

如果你做TCP通訊或者map集合操作,并發處理等功能時,很容易出現?Java?內存溢出的問題。本篇文章,帶領大家深入jvm,分析并找出jvm內存溢出的代碼。

jvm中除了程序計數器,其他的區域都有可能會發生內存溢出

內存溢出是什么

當程序需要申請內存的時候,由于沒有足夠的內存,此時就會拋出OutOfMemoryError,這就是內存溢出

內存溢出和內存泄漏有什么區別

內存泄漏是由于使用不當,把一部分內存“丟掉了”,導致這部分內存不可用。?
當在堆中創建了對象,后來沒有使用這個對象了,又沒有把整個對象的相關引用設為null。此時垃圾收集器會認為這個對象是需要的,就不會清理這部分內存。這就會導致這部分內存不可用。?
所以內存泄漏會導致可用的內存減少,進而會導致內存溢出。

用到的jvm參數

下面為了說明溢出的情景,會執行一些實例代碼,同時需要給jvm指定參數

-Xms 堆最小容量(heap min size)

-Xmx 堆最大容量(heap max size)

-Xss 棧容量(stack size)

-XX:PermSize=size 永生代最小容量

-XX:MaxPermSize=size 永生代最大容量

堆溢出

堆是存放對象的地方,那么只要在堆中瘋狂的創建對象,那么堆就會發生內存溢出。

下面做一個堆溢出的實驗?
執行這段代碼的時候,要給jvm指定參數

1?
2?
3?
4?
5?
6?
7?
8?
9?
//jvm參數:-Xms20m -Xmx20m?
public class HeapOOMTest {?
public static void main(String[] args){?
LinkedList xttblog=new LinkedList();//作為GC Root?
while(true){?
xttblog.add(new HeapOOMTest());//瘋狂創建對象?
}?
}?
}?
-Xms20m -Xmx20m作用是將jvm的最小堆容量和最大堆容量都設定為20m,這樣就不會動態擴展jvm堆了?
這段代碼瘋狂的創建對象,雖然對象沒有聲明變量名引用,但是將對象添加到隊列l中,這樣隊列l就持有了一份對象的引用?
通過可達性算法(jvm判斷對象是否可被收集的算法)分析,隊列l作為GC Root,每一個對象都是l的一個可達的節點,所以瘋狂創建的對象不會被收集,這就是內存泄漏,這樣總有一天堆就溢出了。

運行結果:

Exception in thread “main” java.lang.OutOfMemoryError:?Java?heap space at java.util.LinkedList.linkLast(Unknown Source) at java.util.LinkedList.add(Unknown Source) at test.HeapOOMTest.main(HeapOOMTest.java:23)?
程序發生內存溢出,并提示發生在Java heap space

分析解決方法

思路?
用visualVM工具分析堆快照?
如果發生內存泄漏:?
step1:找出泄漏的對象?
step2:找到泄漏對象的GC Root?
step3:根據泄漏對象和GC Root找到導致內存泄漏的代碼?
step4:想法設法解除泄漏對象與GCRoot的連接?
如果不存在泄漏:

看下是否能增大jvm堆的最大容量

優化程序,減小對象的生命周期

前期準備?
當發生堆溢出的時候,可以讓程序在崩潰時產生一份堆內存快照?
產生堆內存快照的方法:?
給jvm加上參數XX:+HeapDumpOnOutofMemoryError,這樣就會在程序崩潰的時候,產生一份堆內存快照?
分析堆內存快照我建議用jdk自帶的可視化監視工具visualVM,位置在jdk安裝目錄下的bin,如果是在Linux環境的話,可以把快照傳到window。因為分析工具會占用很大的內存,不建議在服務端進行分析。

實戰?
下面對剛才程序產生的堆內存快照進行分析。?
打開visualVM,裝入剛剛生成的快照,打開類標簽頁?
visualVM?
隊列和瘋狂創建的對象幾乎占滿了整個棧,想要讓垃圾收集器回收這些對象,要讓他們與GC Root斷開連接?
雙擊HeapOOMTest類,跳轉到實例標簽頁,可以查看這個類的所有實例?
在實例上右鍵——顯示最近的垃圾回收根節點,可以看到這個對象與根節點的連接?
StackOverFlowError?
只要斷開HeapOOMTest對象與LinkedList的連接,這些瘋狂創建的對象就會被收集了

棧溢出?
調用方法的時候,會在棧中入棧一個棧幀,如果當前棧的容量不足,就會發生棧溢出StackOverFlowError?
那么只要瘋狂的調用方法,并且有意的不讓棧幀出棧就可以導致棧溢出了。

下面來一次棧溢出

1?
2?
3?
4?
5?
6?
7?
8?
9?
10?
//jvm參數:-Xss128k?
public class StackSOFTest {?
public void stackLeak(){?
stackLeak();//遞歸,瘋狂的入棧,有意不讓出棧?
}?
public static void main(String[] args){?
StackSOFTest s=new StackSOFTest();?
s.stackLeak();?
}?
}?
jvm設置參數-Xss128k,目的是縮小棧的空間,這樣棧溢出“來的快一點”?
程序中用了遞歸,讓棧幀瘋狂的入棧,又不讓棧幀出棧,這樣就會棧溢出了。

運行結果:

Exception in thread “main” java.lang.StackOverflowError at test.StackSOFTest.stackLeak(StackSOFTest.java:17) at test.StackSOFTest.stackLeak(StackSOFTest.java:17)?
運行時常量池溢出

這里儲存的是一些常量、字面量。如果運行時常量池內存不足,就會發生內存溢出。從jdk1.7開始,運行時常量池移動到了堆中,所以如果堆的內存不足,也會導致運行時常量池內存溢出。

下面來一次運行時常量池溢出,環境是jdk8?
只要創建足夠多的常量,就會發生溢出

1?
2?
3?
4?
5?
6?
7?
8?
9?
10?
11?
12?
13?
14?
15?
16?
/**?
* jvm參數:?
* jdk6以前:-XX:PermSize=10M -XX:MaxPermSize=10M?
* jdk7開始:-Xms10m -Xmx10m?
* */?
public class RuntimePoolOOM {?
public static void main(String[] args){?
int i=1;?
//保持常量的引用,防止被fullgc收集?
LinkedList xttblog=new LinkedList();?
while(true){?
//將常量添加到常量池?
xttblog.add(String.valueOf(i++).intern());?
}?
}?
}?
因為jdk6以前,運行時常量池是在方法區(永生代)中的,所以要限制永生代的容量,讓內存溢出來的更快。?
從jdk7開始,運行時常量池是在堆中的,那么固定堆的容量就好了?
這里用了鏈表去保存常量的引用,是因為防止被fullgc清理,因為fullgc會清理掉方法區和老年代?
intern()方法是將常量添加到常量池中去,這樣運行時常量池一直都在增長,然后內存溢出

運行結果:

Exception in thread “main” java.lang.OutOfMemoryError: Java heap space

at java.lang.Integer.toString(Unknown Source)at java.lang.String.valueOf(Unknown Source)at test.RuntimePoolOOM.main(RuntimePoolOOM.java:30)

提示在heap區域發生內存溢出,果然運行時常量池被移到了堆中

方法區溢出

方法區是存放類的信息,而且很難被gc,只要加載了大量類,就有可能引起方法區溢出?
這里將不做演示了,想試試的可以用cglib創建大量的代理類

分析

工作中也有可能會遇上方法區溢出:?
當多個項目都有相同jar包的時候,又都存放在WEB-INF\lib\下,這樣每個項目都會加載一遍jar包。會導致方法區中有大量相同類(被不同的類加載器所加載),又不會被gc掉。

解決方案

在應用服務器中建立一個共享lib庫,把項目中常用重復的jar包存放在這里,項目從這里加載jar包,這樣就會大大減少類加載的數量,方法區也“瘦身”了
如果實在不能瘦身類的話,那可以擴大方法區的容量,給jvm指定參數-XX:MaxPermSize=xxxM

轉載于:https://www.cnblogs.com/lvgg/p/6840644.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/252700.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/252700.shtml
英文地址,請注明出處:http://en.pswp.cn/news/252700.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

一個go1.9.x 編譯器內聯引起的棧信息錯亂的問題分析

2019獨角獸企業重金招聘Python工程師標準>>> 背景是在寫個日志庫,日志庫有個很重要的功能就是要打印出調用棧,知道具體是哪個文件,哪個函數調用的Info 等。 然后在測試中發現了一種寫法,我自己本機測試一直ok&#xff…

CMOS Sensor的調試經驗分享

轉自:http://bbs.52rd.com/forum.php?modviewthread&tid276351 CMOS Sensor的調試經驗分享      我這里要介紹的就是CMOS攝像頭的一些調試經驗。   首先,要認識CMOS攝像頭的結構。我們通常拿到的是集成封裝好的模組,一般由三個部…

Learn Python—表達式、數據類型、流程控制

表達式 在 Python 中,2 2 稱為“表達式”,它是語言中最基本的編程結構。表達式包含“值”(例如2)和“操作符”(例如),并且總是可以求值(也就是歸約)為單個值。這意味著在…

監控工具之zabbix server3.4 部署配置

[rootlocalhost src]# cat /etc/redhat-release CentOS Linux release 7.5.1804 (Core) [rootlocalhost src]# pwd /usr/local/src 配置zabbix的yum源 [rootlocalhost src]# rpm -ivh http://repo.zabbix.com/zabbix/3.4/rhel/7/x86_64/zabbix-release-3.4-2.el7.noarch.rpm …

CMOS Sensor基礎知識

CMOS Sensor基礎知識 曝光時間以行長為單位; PCLK以Hz為單位; 行長以周期數為單位,幀長以行長數為單位;其中周期數就是頻率 T 周期以ms為單位; f 頻率以Hz為單位; f 1 / T; Vsync Dummy Line…

java獲取mp3的時長和播放mp3文件

所需包為jaudiotagger-2.2.6-SNAPSHOT.jar和jl1.0.1.jar。 import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream;import org.jaudiotagger.audio.AudioFileIO; import org.jaudiotagger.audio.mp3.MP3AudioHeader; import org.jaudiotag…

Redis 優缺點

REmote DIctionary Server(Redis) 是一個由Salvatore Sanfilippo寫的key-value存儲系統。 Redis是一個開源的使用ANSI C語言編寫、遵守BSD協議、支持網絡、可基于內存亦可持久化的日志型、Key-Value數據庫,并提供多種語言的API。 Redis 與其他 key - value 緩存產品…

Python并發編程之concurrent.futures

2019獨角獸企業重金招聘Python工程師標準>>> concurrent.futures模塊提供了一個異步執行callables的高級接口。 可以使用ThreadPoolExecutor和ProcessPoolExecutor。 兩者都繼承了相同的接口,該接口由抽象的Executor類定義。 一個抽象類,提供…

1.3鏈表

鏈表的物理存儲結構是用一組地址任意的存儲單元存儲數據的。不像順序表占據連續的一段內存空間,而是將存儲單元分散在內存的任意地址上。 鏈表結構中,每個數據元素記錄都存放到鏈表的一個節點(node)中,而每個節點之間由…

移植opencv3.20到3556AV100

1.移植環境: Ubuntu14.04 arm-hisiv200-linux-opencv3.20 下載地址 2.移植步驟: 1)安裝cmake-gui 2)新建一個opencv目錄存放opencv-3.2.0.zip,并解壓 擊Browse Source選擇~/hisi/opencv/opencv-3.2.0 點擊Brow…

ngnix 詳解

4 Nginx的rpm軟件包安裝 4.1 安裝包在位置 D:\講課內容--\新巴巴運動網\nginx高并發解決\nginx安裝包 4.2 此種安裝方式不用安裝gcc等編譯工具 4.3 安裝命令如下 rpm –ivh nginx 5 配置虛擬主機 5.1 什么是虛擬主機 虛擬主機是一種特殊的軟硬件技術,它可以將網絡上…

iscroll5制作上下拉刷新 tab出現的問題

1.iscoll5插件刷新后如果想改變現實位置如果向下幾px可以用 myScroll.scrollBy(0,0);方法,該值是相對當前位置。 2.iscoll5用到tab的時候,用點擊生成iscoll對象出現取消不了之前的對象的綁定事件,點擊多次后刷新執行多次的問題,解…

初談邏輯讀、物理讀、預讀

前言: 該文并不全是本人原創,里面的某些原理來自于CareySon。 SQL SERVER數據存儲的形式 要理解邏輯讀、物理讀、預讀這三個概念,先要搞懂SQL Server的數據存儲方式。 SQL Server數據庫包括數據文件和日志文件,一個數據庫可以有一…

Makefile常用萬能模板(包括靜態鏈接庫、動態鏈接庫、可執行文件)

1、生成可執行文件的makefile2、生成靜態鏈接庫的makefile3、生成動態鏈接庫的makefile 本文把makefile 分成了三份:生成可執行文件的makefile,生成靜態鏈接庫的makefile,生成動態鏈接庫的makefile。 這些makefile都很簡單,一般都…

TSQLDBServerHttpApi使用工作線程池

TSQLDBServerHttpApi使用工作線程池 TSQLDBServerHttpApi創建時,默認是使用單線程模式,且只使用一個數據庫連接,服務端要應對眾多的客戶端只靠一個工作線程(主線程)和一個數據庫連接, 服務端主線程不忙死才…

hibernate

Hibernate是一個開放源代碼的對象關系映射框架,他對JDBC進行了輕量級的封裝,使Java開發員可以隨心所欲的使用對象編程思維操作數據庫。 SessionFactory接口負責初始化Hibernate.他充當數據儲存源的代理,并負責創建Session對象。 Session&…

Python數據分析之pandas入門

一、pandas庫簡介 pandas是一個專門用于數據分析的開源Python庫,目前很多使用Python分析數據的專業人員都將pandas作為基礎工具來使用。pandas是以Numpy作為基礎來設計開發的,Numpy是大量Python數據科學計算庫的基礎,pandas以此為基礎&#x…

激光雷達和毫米波雷達的區別

什么是激光雷達 激光雷達,是以發射激光束探測目標的位置、速度等特征量的雷達系統。其工作原理是向目標發射探測信號(激光束),然后將接收到的從目標反射回來的信號(目標回波)與發射信號進行比較&#xff0c…

Git—使用方法

1、:插件的安裝(eclipse LUNA版本之后已經自動集成,不需要安裝插件)、 * 先打開該網頁提供了對應版本的EGit,自己選擇相應的版本。(http://wiki.eclipse.org/EGit/FAQ#Where_can_I_find_older_releases_of_EGit.3F&…

激光雷達與毫米波雷達對比

激光雷達是一種采用非接觸激光測距技術的掃描式傳感器,其工作原理與一般的雷達系統類似,通過發射激光光束來探測目標,并通過搜集反射回來的光束來形成點云和獲取數據,這些數據經光電處理后可生成為精確的三維立體圖像。采用這項技…