`
tangay
  • 浏览: 94071 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

让Web server避免outofmemory

阅读更多

由于使用Jetty+Jersey做为RESTful server,由于用户通过REST API访问后台服务时,单个用户一次提交或者取得的数据最大值为50M,假设jvm的heap size最大值为1G的话,如果并发的用户数过多,后台就很可能出现outofmemory的error。为了避免这种情形发生,想出了一个初步方案:

 

1 配置一个filter,并每个用户请求都会经由filter的filter方法处理

2 每次filter方法调用的,都会检查jvm的heap memory的使用情况,如果heap memory使用率到达了80%以上,则调用System.gc(),并拒绝访问,返回503错误,告知client端。

 

代码如下:

private void checkServerMemoryUsage() {
		MemoryMXBean aMemoryMXBean=ManagementFactory.getMemoryMXBean();
		MemoryUsage heapMemoryUsage=aMemoryMXBean.getHeapMemoryUsage();
		long usedHeapMemory = heapMemoryUsage.getUsed();
		long maxHeapMemory = heapMemoryUsage.getMax();
		double MemUsedRate = (usedHeapMemory*1.0)/maxHeapMemory;

		if(MemUsedRate >= 0.8) {
			System.gc();
			logger.error("Heap memory will be expired. Used Memory is: "+maxHeapMemory+" bytes. The max heap memory is: "+maxHeapMemory+" bytes.");
			throw new RestException(503, "Server is too busy!");
		}
	}

 

本来以为这样没问题了,可是后来在调试的过程中发现这样一个现象:

模拟10个客户端,同时向server发请求,每个客户端每次提交的数据为5M,每个客户端连续发5000次,运行过程中把-verbosegc选项打开,开始时,情况很正常,gc动作经常进行(这是由于新生代的Eden区域经常满的缘故,触发了minor GC),由于后台还会把用户提交的数据进行负责处理,也会产生很多新的对象,所以堆的已用区越来越大,但使用率到达80%的时候,显式的调用了System.gc(),这时候系统会执行次full gc,并且客户端也会收到503错误。一切看起来比较完美。

 

当我把所有客户端线程都kill掉的时候,问题来了,heap的占用率还在缓慢增长(因为前面提交的数据可能还在处理),但是minor gc并不触发了(Eden区域未满就不会触发),可见大量的对象在年老代区(Old generation),而年老代又没有被写满,不会被触发full gc(默认配置是1小时才会定时执行一次full gc),这时候heap已用率会长期维持在高位。

 

于是一个改进方案出现了:

写道
static class MemoryInfo{
long usedHeapMemory;
long maxHeapMemory;
double getUsageRate(){
return (usedHeapMemory*1.0)/maxHeapMemory;
}
}

private void checkServerMemoryUsage() {
MemoryInfo info = getHeapUssageRate();
if(info.getUsageRate() >= 0.8) {
System.gc();
info = getHeapUssageRate();
if(info.getUsageRate() >= 0.8) {
logger.error("Heap memory will be expired. Used Memory is: "+info.usedHeapMemory+" bytes. The max heap memory is: "+info.maxHeapMemory+" bytes.");
throw new RestException(503, "Server is too busy!");
}
}
}

private MemoryInfo getHeapUssageRate() {
MemoryInfo info = new MemoryInfo();
MemoryMXBean aMemoryMXBean=ManagementFactory.getMemoryMXBean();
MemoryUsage heapMemoryUsage=aMemoryMXBean.getHeapMemoryUsage();
info.usedHeapMemory = heapMemoryUsage.getUsed();
info.maxHeapMemory = heapMemoryUsage.getMax();
return info;
}

 

每次请求来的时候,检查heap使用情况,如果使用率到达80%了,马上调用Sytem.gc()执行full gc。然后再次判断看使用率是否超过80%,如果还是超过的话,则拒绝服务,返回503错误。

 

进一步改进:

 

因为full gc会耗时较长,经常调用会影响服务器的性能(调用时别的线程会被挂起)。分析应用情况,发现临时数据缓存占内存占用的大多数,这样调大server的jvm的新生代在堆中的比例,这样只会让minor gc调用比较频繁,而由System.gc()引发的full gc次数就大大减少了。

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics