服务遇到内存溢出,定位以及解决办法

Posted exodus3

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了服务遇到内存溢出,定位以及解决办法相关的知识,希望对你有一定的参考价值。

之前遇到了本地服务运行正常,但是上了测试环境之后,就报出了内存溢出的异常,也就是OutOfMemoryError异常.
经过日志上的定位,找到代码大概的位置

业务逻辑是这样的:读取文件的内容,将每一行的内容,进行替换指定的内容。

public void testReplace(File file) {
	// ....		
    // 传过来的文件 File file进行处理,进行一些业务处理,之后拿到content
	List<PpBorderDetail> ppBorderDetailList = dpOrderRepository
				.findByOrderStatusAndBorderStatusAndConditionsWithStock(dpOrderNos, fromWsNo, false,
						DpSplitResultType.BIG_BORDER.getIndex(), DpSplitResultType.SMALL_PANEL_BORDER.getIndex(), true)
				.stream().map(this::genBorderStkOutDetail)
				.collect(Collectors.groupingBy(detail -> detail.getDpOrderNo()
						+ "_" + detail.getDpBorderItemNo() + "_" + detail.getCalcuLength()))
				.entrySet().stream().map(this::genBorderStkOutDetail)
				.sorted(Comparator.comparing(PpBorderDetail::getDpTypeName)
						.thenComparing(PpBorderDetail::getDpBorderItemName)
						.thenComparing(PpBorderDetail::getDeliveryDate).thenComparing(PpBorderDetail::getDpOrderNo))
				.collect(Collectors.toList());
	  
	// 对文件里的文本内容进行对应的替换
	String s;
	// 模拟大概的情况
	for(PpBorderDetail ppBorderDetail : ppBorderDetailList) {
		for (String str: ppBorderDetail.getContent()) {
			s = str.replace("\\r", "\\r\\n");
			// 后续对s进行了相关的操作
		}
	}
}

乍看之下,没毛病,传过来的是File文件,然后对文件里面的内容进行处理。跟测试沟通之后才发现,传过来的是一个大文件。我在想,如果一个大文件,要处理的文本内容,里面有几万行,甚至几十万行,这些处理应该是没问题的。
服务器性能好,对这些压力扛得住,但是怎么就报出了OutOfMemoryError异常了呢?根据查阅一些资料,大量的String的处理,产生了大量的对象,JVM来不及回收,就会报出这种异常了。于是乎,对上面的代码进行了一些改动。

public void testReplace(File file) {
	// ....		
    // 传过来的文件 File file进行处理,进行一些业务处理,之后拿到content
	List<PpBorderDetail> ppBorderDetailList = dpOrderRepository
				.findByOrderStatusAndBorderStatusAndConditionsWithStock(dpOrderNos, fromWsNo, false,
						DpSplitResultType.BIG_BORDER.getIndex(), DpSplitResultType.SMALL_PANEL_BORDER.getIndex(), true)
				.stream().map(this::genBorderStkOutDetail)
				.collect(Collectors.groupingBy(detail -> detail.getDpOrderNo()
						+ "_" + detail.getDpBorderItemNo() + "_" + detail.getCalcuLength()))
				.entrySet().stream().map(this::genBorderStkOutDetail)
				.sorted(Comparator.comparing(PpBorderDetail::getDpTypeName)
						.thenComparing(PpBorderDetail::getDpBorderItemName)
						.thenComparing(PpBorderDetail::getDeliveryDate).thenComparing(PpBorderDetail::getDpOrderNo))
				.collect(Collectors.toList());
	  
	// 对文件里的文本内容进行对应的替换
	String s;
	// 模拟大概的情况
	for(PpBorderDetail ppBorderDetail : ppBorderDetailList) {
		for (String str: ppBorderDetail.getContent()) {
			if (str.startsWith("\\r")) {
                s = str.replace("\\r", "\\r\\n");
                // 后续对s进行了相关的操作
            }
		}
	}
}

增加了相关的逻辑判断,这样不用每次都进行处理,产生临时对象,这是我猜想的。验证的话,还是等发布测试环境之后才能知道最后的结果,果然,测试大文件的内容,没有报错内存溢出的问题了。
这是一次工作经验之谈,看过网上其他的解决办法,大部分都是调JVM参数什么的,我想,还是根据个人的实际工作来谈吧,并不是每次都要照着别人的来做,可以学习别人的,借鉴别人的,但具体问题还是根据自己的实际情况来定的。

以上是关于服务遇到内存溢出,定位以及解决办法的主要内容,如果未能解决你的问题,请参考以下文章

Java中OutOfMemoryError(内存溢出)的三种情况及解决办法

Java 出现内存溢出的定位以及解决方案

Java中OutOfMemoryError(内存溢出)的三种情况及解决办法

Java中OutOfMemoryError(内存溢出)的三种情况及解决办法

强如 Disruptor 也发生内存溢出?

Jmeter之内存溢出解决办法