1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > 内存溢出问题排查及 jvm调参

内存溢出问题排查及 jvm调参

时间:2021-09-06 13:27:02

相关推荐

内存溢出问题排查及 jvm调参

jvm 性能问题

JVM 出现性能问题时表现上是 CPU100%,内存一直占用

1、 如果 CPU 的 100%,要从两个角度出发,一个有可能是业务线程疯狂运行,比如说想很多死循环。还有一种可能性,就是 GC 线程在疯狂的回收,因为 JVM 中垃圾回收器主流也是多线程的,所以很容易导致 CPU 的 100%

2、 在遇到内存溢出的问题的时候,一般情况下我们要查看系统中哪些对象占用得比较多,在实际的业务代码中,找到对应的对象,分析对应的类,找到为什么这些对象不能回收的原因,就是我们前面讲过的可达性分析算法,JVM 的内存区域,还有垃圾回收器的基础

oom内存溢出排查

先看错误日志,如果错误日志能够定位出哪个类对象导致内存溢出,针对问题改 bug就好。但很多时候单凭日志无法定位出内存溢出问题

1. top 命令对当前服务器内存大致了解

top 后 shift+M 按内存占用大到小排序,RES 是此进程实际占用内存,%MEM是占服务器总内存的49.8%

2、利用ps命令查看应用服务 pid

[root@speedyao java]# ps -aux|grep java

3、利用 jstat 查看虚拟机 gc 情况

jstat -gcutil [ []

vmid:虚拟机进程号

interval:采样时间,默认单位是ms

count:采样条数

[root@speedyao java]# jstat -gcutil 17561 1000 10

以上命令代表1秒钟采样1次,总共采样10次。

FULL GC 明显大于 YOUNG GC 次数,并且 FULL GC 次数很频繁,说明程序有大内存对象,并且一直无法释放

4、利用 jmap 直接生成 dump 文件

JVM 会将整个 heap 的信息 dump 写入到一个文件,heap 如果比较大的话,就会导致这个过程比较耗时,并且执行的过程中为了保证 dump 的信息是可靠的,会暂停应用。

[root@speedyao java]# jmap -dump:format=b,file=heap.prof 17561

format=b:表示生成二进制类型的dump文件

file=:后面写的是输出的dump文件路径

17561:jvm 进程 id

5、分析dump 文件 (MAT 或者 jvisualvm)

1、用 MAT (MemoryAnalyzer Tool 为 eclipse 做的)分析 dump 文件,

2、用 jdk 自带的 jvisualvm.exe 来分析 dump 文件, 在 jdk 安装目录找到 jvisualvm.exe 双击打开即可

MAT 分析如下

上图是概要,阴影部分就是大内存对象类,点击选择 “list Object”、“with incoming references”,就出现下图。下图就是这个对象的信息,RunMian 就是map对象所在的类,这样就能快速定位出哪个类中的哪个对象出现了内存异常。

下图Histogram这个tab是堆内存占比从大到小排序。

以上就是内存问题排查的大致步骤。

JVM常用工具(jconsole, jvisualvm,Arthas,MAT)

参考 /weixin_38399962/article/details/103710972

1.jconsole

在命令行中, 输入 jconsole 命令, 即可打开 jconsole,可以监控【本地】或【远程】 java 进程

以本地的一个死锁的 test 进程来讲解,点击你所需要监控的进程,便可以进入监控页面,首页概况

内存中可以看堆和非堆内存的详细信息,

而线程模块可以检查死锁线程,特别简单

比命令行敲 jps 和 jstack 轻松的多

2. jvisualvm

命令行,输入 jvisualvm .即可打开jvisualvm

打开后界面如下:

哇哦, 竟然这么高大上, 不仅仅有jconsole的功能还可以查看dump文件, 并且在首页上还有官方推荐的故障排除指南

让我们感受下它吧,继续用我们的死锁test测试进程

哟吼? 我们发现我们的进程怎么在发出黄色的闪光? 这是警告,我们去线程那一栏一探究竟

原来是出现死锁了, 非常直观,点击线程 dump ,可以看到那个类第几行出现问题了.

监控总览大同小异

监控远程 java 进程 参考 https://rourou./article/details/103717001

3.MAT

参考排查内存溢出步骤 /p/126750819

mat 有 eclipse 的插件,也有独立的安装包

下载 mat 安装包,打开 heap.prof 文件,可以看到mat已经分析了我们的文件

你看他就是个暖男,都帮我们分析出来了一个问题,我们点进去看看

他发现了是ArrayList的问题了,我们再往下看看

看到了嘛,具体代码的位置都帮我们定位好了,那排查也就是手到擒来的事情了。

4.Arthas (阿尔萨斯)

github: /alibaba/arthas

Arthas 是Alibaba开源的Java诊断工具,采用命令行交互模式,是排查jvm相关问题的利器

Arthas 也提供 heapdump 命令导出栈信息, 类似 jmap 命令的heap dump功能,但功能远不止这个

下载安装

curl -O https://alibaba.github.io/arthas/arthas-boot.jarjava -jar arthas-boot.jar

常用命令

version:查看arthas版本号help:查看命名帮助信息cls:清空屏幕session:查看当前会话信息quit:退出arthas客户端---dashboard:当前进程的实时数据面板thread:当前JVM的线程堆栈信息jvm:查看当前JVM的信息sysprop:查看JVM的系统属性---sc:查看JVM已经加载的类信息dump:dump已经加载类的byte code到特定目录jad:反编译指定已加载类的源码---monitor:方法执行监控watch:方法执行数据观测trace:方法内部调用路径,并输出方法路径上的每个节点上耗时stack:输出当前方法被调用的调用路径...... 具体使用参考github帮助文档

强、软、弱、虚引用的区别和使用

参考 https://mp./s/DhqdfanI8VR67J5YFikPsA

强引用(StrongReference)

我们程序中几乎所有的引用使用的都是强引用。

StringBuilder sb = new StringBuilder();

上面通过在堆中创建实例,然后赋值给栈中局部变量 sb 的方式 就是强引用。有如下特点:

强引用可以直接访问目标对象强引用(存在)指向的对象任何时候都不会被回收,JVM 宁愿抛出 OOM 异常,也不会回收。强引用可能会导致内存泄漏

注意:为避免浪费内存,可以在变量 sb 不再使用后通过显示的将变量 sb 置为 null(sb = null),来加速对象的回收。

软引用(SoftReference)

软引用对应的类为 java.lang.ref.SoftReference, 一个软引用中的对象,不会很快被JVM回收,JVM会根据当前堆的使用情况来判断何时回收,当堆的使用率超过阈值时,才回去回收软引用中的对象。

软引用的案例:

Object obj = new Object();SoftReference softRef = new SoftReference<Object>(obj);//删除强引用obj = null;//调用gcSystem.gc();System.out.println("gc之后的值:" + softRef.get()); // 对象依然存在

软引用也可以和一个引用队列联合使用,如果软引用中的对象(obj)被回收,那么软引用会被 JVM 加入关联的引用队列中。

ReferenceQueue<Object> queue = new ReferenceQueue<>();Object obj = new Object();SoftReference softRef = new SoftReference<Object>(obj,queue);//删除强引用obj = null;//调用gcSystem.gc();System.out.println("gc之后的值: " + softRef.get()); // 对象依然存在//申请较大内存使内存空间使用率达到阈值,强迫gcbyte[] bytes = new byte[100 * 1024 * 1024];//如果obj被回收,则软引用会进入引用队列Reference<?> reference = queue.remove();if (reference != null){System.out.println("对象已被回收: "+ reference.get()); // 对象为null}

引用队列(ReferenceQueue)作用

Queue的意义在于我们在外部可以对queue中的引用进行监控,当引用中的对象被回收后,我们可以对引用对象本身继续做一些清理操作,因为我们引用对象(softRef)也占有一定的资源。

弱引用(WeakReference)

弱引用中的对象具有很短的声明周期,因为在系统GC时,只要发现弱引用,不管堆空间是否足够,都会将对象进行回收。由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。

弱引用的简单使用:

Object obj = new Object();WeakReference weakRef = new WeakReference<Object>(obj);//删除强引用obj = null;System.out.println("gc之后的值:" + weakRef.get()); // 对象依然存在//调用gcSystem.gc();System.out.println("gc之后的值:" + weakRef.get()); // 对象为null弱引用也可以和一个引用队列联合使用,如果弱引用中的对象(obj)被回收,那么软引用会被 JVM 加入关联的引用队列中。ReferenceQueue<Object> queue = new ReferenceQueue<>();Object obj = new Object();WeakReference weakRef = new WeakReference<Object>(obj,queue);//删除强引用obj = null;System.out.println("gc之后的值: " + weakRef.get()); // 对象依然存在//调用gcSystem.gc();//如果obj被回收,则软引用会进入引用队列Reference<?> reference = queue.remove();if (reference != null){System.out.println("对象已被回收: "+ reference.get()); // 对象为null}

软引用和弱引用都非常适合保存那些可有可无的缓存数据,当内存不足时,缓存数据被回收(再通过备选方案查询),当内存充足时,也可以存在较长时间,起到加速的作用。

应用

WeakHashMap

当key只有弱引用时,GC发现后会自动清理键和值,作为简单的缓存表解决方案。

ThreadLocal

ThreadLocal.ThreadLocalMap.Entry 继承了弱引用,key为当前线程实例,和WeakHashMap基本相同。

虚引用(PhantomReference)

虚引用 就是 形同虚设 ,它并不能决定 对象的生命周期。任何时候这个只有虚引用的对象都有可能被回收。因此,虚引用主要用来跟踪对象的回收,清理被销毁对象的相关资源。PhantomReference的 get() 方法永远返回 null ,而且只提供了与引用队列同用的构造函数。所以虚引用必须和引用队列一同使用。

Map<Object, String> map = new HashMap<>();ReferenceQueue<Object> queue = new ReferenceQueue<>();Object obj = new Object();PhantomReference phantomRef = new PhantomReference<Object>(obj,queue);map.put(obj,"obj val");new CheckRefQueue(queue,map).start();//删除强引用obj = null;Thread.sleep(1000);int i = 1;while (true){System.out.println("第"+i+"次gc");System.gc();Thread.sleep(1000);}

public class CheckRefQueue extends Thread {private ReferenceQueue queue;private Map<Object, String> map;public CheckRefQueue(ReferenceQueue queue, Map<Object, String> map) {this.queue = queue;this.map = map;}@Overridepublic void run() {// 等待,直到对象呗回收Reference reference = queue.remove();// 释放引用对象的引用map.remove(reference.get());}}

Eclipse设置JVM虚拟内存的三种方式

1. 修改Elipse运行JRE默认JVM参数(全局所有项目)

打开Eclipse,选择Window–Preferences–Java–Installed JREs,编辑选择的jre 在其中的Default VM Arguments: 框中输入 -Xms128m -Xmx512m 等,这样设置 Java 拟虚机内存使用最小是 128M,最大是512M,再单击“OK”

2. 修改Java运行时参数(单个项目)

右击工程 Run AS(Debug As) -->选最下面Run…–> Arguments–>在VM arguments里面填(有内容则追加即可) -Xmx256m 等。这样就可以设置它运行时最大内存为256m

3.修改 apache-tomcat-7.0.76\bin\catalina.bat

第二行加参数即可,用 startup.bat 启动时tomcat时生效的,但是用 eclipse 启动 tomcat 好像不生效

@echo offset JAVA_OPTS=-Xms128m -Xmx350m

4.修改 Eclipse 的配置文件 [eclipse.ini],

其实是影响的 eclipse 自身的运行(注意eclipse开发工具的运行和我们的业务应用运行基本没关系,内存溢出也是我们的业务应用),上面方式修改的是业务应用的运行内存大小,而对我们发生的具体业务应用的内存不产生丝毫应用,所以这一步基本不要做,而且改了也需要关闭 eclipse 再双击启动,直接菜单重启不生效!(eclipse.ini中的设置控制 【Eclipse运行】的JVM。它们对从Eclipse【运行的程序】没有影响。Eclipse在 运行程序时 启动单独的JVM。运行配置设置控制该JVM。对每个程序都有单独的运行配置,以便可以为每个程序设置不同的内容)

${ECLIPSE_HOME}/eclipse.ini-Xms256m-Xmx2048m

内存溢出

1、OutOfMemoryError: PermGen space

PermGen Space指的是内存的永久保存区,该块内存主要是被JVM用来存放class和mete信息的,当class被加载loader的时候就会被存储到该内存区中,与存放类的实例的heap区不同,java中的垃圾回收器GC不会在主程序运行期对PermGen space进行清理。因此,程序启动时如果需要加载的信息太多,超出这个空间的大小,则会发生溢出。解决方案:增加空间分配——增加java虚拟机中的XX:PermSize和XX:MaxPermSize参数的大小,其中XX:PermSize是初始永久保存区域大小,XX:MaxPermSize是最大永久保存区域大小。

2、OutOfMemoryError:Java heap space

heap是Java内存中的堆区,主要用来存放对象,当对象太多超出了空间大小,GC又来不及释放的时候,就会发生溢出错误。即内存泄露越来越严重时,可能会发生内存溢出。解决方案:(1)、检查程序,减少大量重复创建对象的死循环,减少内存泄露。(2)、增加Java虚拟机中Xms(初始堆大小)和Xmx(最大堆大小)参数的大小。

3、StackOverFlowError

stack是Java内存中的栈空间,主要用来存放方法中的变量,参数等临时性的数据的,发生溢出一般是因为分配空间太小,或是执行的方法递归层数太多创建了占用了太多栈帧导致溢出。解决方案:修改配置参数-Xss参数增加线程栈大小之外,优化程序是尤其重要。

四、总结

内存泄漏是堆中的存在无用但可达的对象,GC无法回收。

内存溢出是空间不足的溢出,主要分为 PermGen space 不足、堆不足、栈不足。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。