性能分析-内存泄露

内存泄露--概念如何?

内存泄露一般是存在于Jvm中的,那么JVM中常见的配置参数

• -Xms2048m,初始堆大小,建议<物理内存的1/4,默认值为物理内存的1/64
• -Xmx2048m,最大堆大小,建议与-Xms保持一致,默认值为物理内存的1/4
• -Xmn512m,新生代大小,建议不超过堆内存的1/2
• -Xss256k,线程堆栈大小,建议256k
• -XX:+UseConcMarkSweepGC:开启CMS垃圾回收器z

 

这个配置一般也是在tomcat---bin---catalina.sh文件中设置的,列如:

 

JAVA_OPTS=-XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=64m -Xms512m -Xmx512m -Xmn512m -Xss256k -XX:+UseConcMarkSweepGC -Dcom.sun.management.jmxremote.port=10086 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=192.168.75.129

 

 

首先明白一点:JVM的内存结构:栈内存、堆内存、永久代,但是其中的永久代在java8中就已经被元空间替代,并将元空间移除了JVM,所以目前的JVM就由栈内存和堆内存所构成

内存泄露一般为堆内存和元空间泄露,在java8之前是没有元空间的,是用永久代来表示,但是现在我们基本都是用的java8之后的版本,所以此文分析:内存泄露一般表达为:堆内存泄露和元空间泄露

此外,在堆内存泄露和元空间泄露,一般的泄露方式应该是堆内存,所以我们主要看堆内存

堆内存:

堆内存是JVM中内存空间最大的空间,所有的线程都会共用堆内存,其中堆内存中由新生代和老年代多构成,新生代又分为三个区域:eden,s0(from survivor),s1(to survivor)

所以堆内存=Eden+s0+s1+老年代

其中在堆内存中我们是存放的是数组以及内存对象的实例,比如:

Object a = new Object()
其中,a是存放在栈内存中,new Object()是存放在堆内存中
 
然后我们需要简单了解下垃圾回收
新生代引发的GC为:YoungGC
老年代引发的GC为:FullGC
其中FullGC会引起整个的JVM的用户线程暂停,待垃圾回收完毕之后才能继续运行
垃圾回收:既然是垃圾,那么我们就需要判断什么为垃圾?
在GC中一般会有标记-清除算法、复制算法、标记-压缩算法、分代收集算法,这几种算法都会判断什么垃圾,这个不需要我们去详细了解其原理,做测试嘛,知道怎么回事即可,没必要去深究
垃圾收集器:既然前面有了怎么去判断内存中的垃圾,那么就需要收集器去将其收集起来。
那么在JVM中的垃圾收集器有:
Serial收集器(串行):是一个单线程收集器,用户线程会全部停止
ParNew收集器(并行):是一个并行收集器。Serial收集器的多线程版本
CMS收集器(并发):是一个并发收集器,采用了标记-清除、标记-压缩算法,不过会产生一些内存碎片以及浮动垃圾
G1收集器:在java9中是默认的垃圾收集器
 
上面是一些简单的概念,在分析内存泄露需要知道的知识点,下面我们在了解下到底什么是内存泄露
之前已经说了我们主要关注的是堆内存溢出,那么:
堆内存中存在大量的对象,这些对象都有被引用,当所有对象占用空间达到堆内存的最大值,就会产生内存溢出,日志中还会报错:OutOfMemory:Java heap space
内存溢出的检测工具,图形化工具我常用的的是Jvisualvm,这个工具在之前的文章中也有提到过:java线程监控,还可以使用命令行工具检测
命令行工具:
Jstat –gcutil pid 1000 100
Jmap –histo pid | head -20
Jmap –heap pidw
 
建议图形化工具和命令行工具一起使用
 
那么在内存泄露的一些现象一般有哪些呢?
1、tps出现大浮动的波动,并慢慢降低,甚至降为0,响应时间随之波动,慢慢升高
2、通过jstat命令看到,jvm中的Old区不断增加,FullGC非常频繁,对应的FullGC消耗的时间也不断增加
3、通过Jvisualvm工具可以看到,堆内存曲线不断上升,接近上限时变成一条直线
4、日志报错:java.lang.OutOfMemoryError: Java heap space
同时我们一般在压测中的稳定性场景中,一定要关注内存泄露的情况
下面我们看一下对应的内存泄露的一些图:
1、正常的:有增有降

 

 

 

 2、有问题到图,如图我们可以看到使用到堆越来越多,缓慢上升,并没有之前堆那种有增有减堆趋势

 

 

 

 

 

 我们可以从上图看出来这个tps在缓慢的下降,持续压测的话应该为继续下降,甚至降低到0

 

通过上面我们已经知道了在我们压测过程中已经产生了内存泄露了,那么我们怎么来定位问题呢?

1、通过jvisualvm,进行远程堆dump,然后把dump文件下载下来,用jvisualvm打开进行分析,可
以看到更直观的jvm中对象的信息

 

 

 

 

 

在上面的图中我们一般看的是,cn、com、org这些开头的,这些才是开发自己定义的,像java开头的还有int[]、byte[]等这些都是一些框架或者底层实现的

如果会自己看代码的话那就直接拿到源代码看看这些类中的代码,如果不会看那还是找下开发吧,哈哈

 

最后再附上一些查看Jvm运行状态的命令吧

监控jvm的GC情况
jstat -gcutil pid 1000 100
查看jvm配置信息
jmap -heap pid:可以看到java进程的堆的配置信息,各区的空间大小和配置信息
查看jvm中类和对象的占用情况
jmap -histo 5279 | head -20:查看jvm中各个类的实例数、占用内存数量以及类的全名
堆文件dump
jmap -dump:format=b,file=m.hdump 17777:对堆内存进行dump,以文件的形式进行保存下
来,可以用jvisualvm等工具对文件进行分析

  

 
posted @ 2021-04-01 16:02  大渝  阅读(267)  评论(0编辑  收藏  举报