公司项目java内存分析

公司项目内存占用忽然飙升到90%

开始查询内存占用情况

1:进入服务器使用top查询那个内存占用比较大的进程(Shift + M按内存大小排序  Shift + P 按CPU使用情况排序)

我们发现PID=22977的这个进程占用10.5%的内存,RES(常驻内存)占用1.4G;但是这个程序启动命令是java -Xmx1024m -Xms1024m -jar test-0.1.0-SNAPSHOT.jar --spring.profiles.active=prod,

可以看出分给该程序使用的内存是1G,但是该程序已经超过了分给它的内存,并且其它7个程序的内存分配为512,应占用内存4.5G,但是使用使用7.2G,导致内存报警;

再次使用命令jmap -heap 22977查看该内存的使用情况(因为写文章是排查后的所以不是当时的PID)

可以看出该进程占用内存并未超过分配的1G内存:说明jvm运行良好,并未出现异常

由此分析是jvm使用了堆外内存导致内存占用过多

一、堆外内存的产生条件如下:

  1. 将长期存活的对象移入堆外内存,从而减少垃圾回收期管理的对象数量,为了降低FGC的次数和频率,打到提高系统响应速度的目的;
  2. 加快了复制的速度:堆内在 flush 到远程时,会先复制到直接内存,然后在发送;而堆外内存相当于省略掉了这个工作
  3. 通过Unsafe开辟堆外内存;

     4.通过NIO的ByteBuffer开辟堆外内存;

二、堆外内存是否可控

     虽然还没有查清堆外内存保存了那些内容,但本次目的是解决在8G内存的服务器下,是否可以根据实际情况减少内存使用?JVM可以通过-XX:MaxDirectMemorySize来设置堆外内存的大小。若不对该参数进行设置,则会分配JVM堆同样大小的区域。所以说JVM分配给堆外的内存大小是可控的;

三、堆外内存分配小了是否会OOM

如果我们把堆外内存分配的小一些,那么内存的使用量就会下降,但当要使用堆外内存可已经没有空间的时候,是否会OOM?经过测试得出,堆外内存确实会发生OOM。但堆外内存会被回收,当JVM发现申请堆外内存空间不足时,会调用System.gc()来进行垃圾回收,JVM找到堆外内存是通过堆内的DirectByteBuffer对象,这个对象保存着堆外内存的引用。

四、测试堆外内存会不会造成内存崩溃

public static void main(String[] args) {
        for(int i=0;i<10;i++){
            System.out.println(i);
            ByteBuffer byteBuffer=ByteBuffer.allocateDirect(1*1024*1024);
        }
    }

测试 可以发现内存溢出了

当设置了堆外内存大小为5M,关闭了System.gc()时使用命令-XX:+DisableExplicitGC,每次分配1M的堆外内存,当分配到第6次时,程序发生了OOM。

当打开System.gc()时,则程序可以正常分配10M的内存。

故而可见,控制堆外内存的大小后,当堆外内存不够分配时,会进行垃圾回收,不会发生OOM,除非堆外内存的所有对象都无法释放。

五、堆外内存排查

对系统进行快照:jmap -dump:format=b,file=/dumps/prosche.hprof 22977

使用MemoryAnalyzer将快照打开进行分析可以看出

JVM堆存中有160个DirectByteBuffer,这160个DirectByteBuffer对应的堆外内存对象是否就是那400M,还有待查验

六、解决办法

对所有Java程序生效:

  1. 添加-XX:MaxDirectMemorySize参数,降低堆外内存的使用;
  2. 添加-XX:+HeapDumpOnOutOfMemoryError和-XX:HeapDumpPath=/path/dump,在OOM时打印快照;
  3. 添加-XX:+PrintGC和-XX:+PrintGCDetails和-xloggc:/path/gc.log,在垃圾回收时记录日志
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值