java实战内存溢出

内存溢出问题

在实际开发过程中,有可能会遇到内存溢出的错误,如java.lang.OutOfMemoryError: Java heap space,也就是堆内存溢出,这个时候就得排查到底是什么导致了内存溢出,一般情况下,有以下两种情况

  • 内存预分配空间不足
  • 内存泄露

内存分配空间不足,说明启动程序之前对程序整体的空间没有一个很好的预测导致内存空间太小,但是笔者认为这种情况很少见,所以更多的情况下可能是发生了内存泄露。

这里的内存泄露并不是说你将对象清空后发生GC时没有将其回收,而是说从程序逻辑上看本应该释放掉内存的地方,仍然使用强引用进行引用着,导致GC回收器错误的认为不需要回收从而发生内存泄露。说白了,如果发生内存泄露,那就是程序员的错,就像 发生RuntimeException就是程序员的问题。

导出内存映像文件

那么当发生内存泄露时应该如何进行排查呢?这里也有两种思路

  • 内存溢出时自动生成堆内存映像文件
  • 使用 jmap生成堆内存映像文件

先说第一种:当发生内存溢出时自动生成堆内存文件,这种方法在我看来就是为了以防万一才进行设置,参数设置为

-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=./

分别表示当内存溢出时导出内存映像文件,以及导出的文件的位置。然而在实际生产环境中如果等到内存溢出时才进行排查,那么代价是巨大的,而且当检测到程序运行时内存使用异常,我们希望能够及时的检测到程序内存的使用情况,这就需要一种动态的导出内存映像文件的方法,这就是jmap,先看一下jmap的使用

Usage:
    jmap [option] <pid>
        (to connect to running process)
    jmap [option] <executable <core>
        (to connect to a core file)
    jmap [option] [server_id@]<remote server IP or hostname>
        (to connect to remote debug server)

where <option> is one of:
    <none>               to print same info as Solaris pmap
    -heap                to print java heap summary
    -histo[:live]        to print histogram of java object heap; if the "live"
                         suboption is specified, only count live objects
    -clstats             to print class loader statistics
    -finalizerinfo       to print information on objects awaiting finalization
    -dump:<dump-options> to dump java heap in hprof binary format
                         dump-options:
                           live         dump only live objects; if not specified,
                                        all objects in the heap are dumped.
                           format=b     binary format
                           file=<file>  dump heap to <file>
                         Example: jmap -dump:live,format=b,file=heap.bin <pid>
    -F                   force. Use with -dump:<dump-options> <pid> or -histo
                         to force a heap dump or histogram when <pid> does not
                         respond. The "live" suboption is not supported
                         in this mode.
    -h | -help           to print this help message
    -J<flag>             to pass <flag> directly to the runtime system

上述信息通过使用 jmap -help得到,主要使用的就是-dump中的format=b以及file=<file>,这里的<pid>指的是java线程id,通过使用 jps -l可以列出当前正在运行的 java线程信息及对应的 pid号

内存溢出简单例子

我们先从start.spring.io网站生成一个基于maven的springboot项目,选择web即可。导入到 IDE 中,创建一个controller,在其中添加方法,代码如下

package com.zdb.memoryinpractice.controller;

import com.zdb.memoryinpractice.pojo.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@RestController
@RequestMapping("/memory")
public class MemoryClass {
	
	private List<User> list = new ArrayList<>();
	
	@RequestMapping("/heap")
	public String heap() {
		while(true) {
			list.add(new User(UUID.randomUUID().toString(), 10));
		}
	}
}

通过死循环添加内存,这里我设置的堆内存大小为 32M,通过参数-Xmx32m-Xms32m进行设置,运行一段时间后错误如下
堆内存溢出

首先使用 jps -l查看打开的 java 线程信息,显示如下

// 插入图片

可以发现,我的打开的 java 线程为 com.zdb.memoryinpractice.MemoryInPracticeApplication,对应的 pid 号为 16420,接下来就通过 jmap将它的堆内存映像导出

// 插入图片

到此,堆内存映像文件已经导出,接下来就是分析这个内存文件

分析内存映像文件

内存映像文件已经导出,该如何分析这个文件定位问题?我们使用一个叫做 MAT的分析工具,可以从官网免费下载,并且是跨平台的。打下载之后打开,将映像文件载入进行分析

// 插入图片

点击红色圈住的选项,生成一个列表,记录了内存使用情况,选择占用内存特别多的项

// 插入图片

在这里插入图片描述

就能得到如图的调用关系,至此,大部分问题也能分析出来

方法区内存溢出

方法区内存溢出一般很少见,在以前的版本中叫做永久代,在 java 8中改做元空间metaspace,如果加载的类太多的话,或者常量信息太多也是有可能发生内存溢出的,检测方法和堆内存溢出类似,这里不再演示

总结

以上展示了当发生内存溢出时基本的解决思路,当然因为是我们手动构造的,所以问题很容易就能定位出来,在实际可能情况更加的复杂,要学会使用工具进行问题排查,使用多了才能快速的定位到问题,解决问题

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值