求助>求助OutOfMemoryError:MetaSpace>
10回复

求助OutOfMemoryError:MetaSpace



生产环境系统里有400多个groovy脚本,可以通过管理端修改数据库中的脚本,并发布(读取groovy脚本并编译成script对象,缓存到内存),大概逻辑如下

    //1、创建临时的存放script的tempMap
    Map<String,Script>tempMap=new HashMap();
    //2、查数据库得到脚本文本
    Map<String,String> 
   scriptTextMap=scriptService.selectAll();
   //3、遍历id:scriptTextMap,生成Script对象,大概代码如下
   for(String id:scriptTextMap.keySet()){
       GroovyShell shell=new GroovyShell(new Binding());
        Script script= shell.parse(scriptTextMap.get(id));
        tempMap.put(id,script);
   }
   //4、全局scriptMap替换为tempMap
    scriptMap=tempMap;
}

元数据空间128m,最大256m,堆3g,新生代2g,cms收集器
问题:运行一段时间后,load方法会抛 OutOfMemoryError:MetaSpace,查看gc日志,发现full gc前后,metaSpace used大概120M,capacity 171M,committed 248M。
上网搜了一些回答,有的说是 由于每次parse都会创建新的类加载器,每个类加载器即使加载一个对象,也会在metaSpace中占据一个内存块。但是,每次load执行后,旧的scriptMap就会被回收掉,旧的Script对象也会被回收,编译groovy生产的类应该也可以被卸载,full gc时,MetaSpace中的空间应该能够被回收,请各位大佬们帮忙看看。

2636 阅读
请先登录,再评论

回复列表

这是典型的内存碎片化导致的OOM,由于Metaspace的管理方式是以VirtualPage(256KB)为回收粒度。一个page被划分成多个Chunk供ClassLoader使用,同时一个Page可以被多个ClassLoader使用,当ClassLoader被卸载后释放的内存会优先到ChunkManager中重用,只有当整个Page中所有的chunk都释放了才有可能释放Page。
从这个描述可以看出Metaspace在长时间使用以后必然存在大量的碎片化,我曾经遇到碎片化高达50%。目前这样的场景暂时无解。可以尝试:
1.继续增大metaspace
2.定时重启
3.从application层面减少频繁的元数据使用

1
https://a.perfma.net/img/2382850
鸠摩1月前

是应该被卸载,不过这好像是Bug,导致有了强引用而卸载不了,看这个:https://blog.csdn.net/chirui2705/article/details/100647559/

2
回复 鸠摩:
public class TestOOM{

	public static String text="def eval(){}";
	
	@Test
	public void buildScript(){
		Map map=new HashMap<>();
		int i=0;
		while(true){
			System.out.println(++i);
		
			Binding binding=new Binding();
			GroovyShell shell=new GroovyShell(binding);
			Script script=shell.parse(text);
			map.put(i,script);
			if(i%500==0){
				map.clear();
			}
		}
	}
}

测试时,jvm主要参数如下,由于测试时,加载的类比较少,Metaspace设置为20M,跑一会Metaspace就OOM了,如果脚本里的内容多一点,就更容易OOM
-XX:MetaspaceSize=20m -XX:MaxMetaspaceSize=20m -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=6 -XX:SurvivorRatio=6 -XX:+DisableExplicitGC -XX:CMSInitiatingOccupancyFraction=85 -XX:+UseCMSInitiatingOccupancyOnly -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+CMSParallelInitialMarkEnabled -XX:+CMSParallelRemarkEnabled -XX:+CMSScavengeBeforeRemark -

回复
鸠摩1月前
回复 会飞的鱼_961409:

你可以写个完整的实例发出来,我有空也模拟模拟

回复
鸠摩1月前
回复 会飞的鱼_961409:

所以我觉得是空间碎片化,也就是Metachunk太多,但是总是无法合并成一个大的空间,当某个需要大块的类加载器过来时,不得不重新申请新空间导致的

回复
查看更多