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指定参数










//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 
那么只要疯狂的调用方法,并且有意的不让栈帧出栈就可以导致栈溢出了。

下面来一次栈溢出










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 
只要创建足够多的常量,就会发生溢出










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.mzph.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&…

激光雷达与毫米波雷达对比

激光雷达是一种采用非接触激光测距技术的扫描式传感器,其工作原理与一般的雷达系统类似,通过发射激光光束来探测目标,并通过搜集反射回来的光束来形成点云和获取数据,这些数据经光电处理后可生成为精确的三维立体图像。采用这项技…