现象
生产上Perm配置了400M,发生了java.lang.OutOfMemoryError: PermGen space
。然调整了600M,没过过久,还是OOM了。
分析过程
增加启动参数 -verbose, 将类加载的日志进行输出,进行观察。
[Loaded ma.glasnost.orika.generated.Orika_OrderBase_CreateOrderReq_Mapper2063753710 from __JVM_DefineClass__]
[Loaded ma.glasnost.orika.generated.Orika_ServiceDefinitionParameter_ServiceDefinitionParameterReq_Mapper458086693 from __JVM_DefineClass__]
[Loaded ma.glasnost.orika.generated.Orika_ServiceDefinition_ServiceDefinitionReq_Mapper925591567 from __JVM_DefineClass__]
[Loaded ma.glasnost.orika.generated.Orika_ActivityDefinition_ActivityDefinitionReq_Mapper8350956 from __JVM_DefineClass__]
[Loaded ma.glasnost.orika.generated.Orika_ServiceDefinition_ServiceDefinitionReq_Mapper786907469 from __JVM_DefineClass__]
[Loaded ma.glasnost.orika.generated.Orika_OrderBase_CreateOrderReq_Mapper1746474167 from __JVM_DefineClass__]
[Loaded ma.glasnost.orika.generated.Orika_ActivityDefinitionDetail_ActivityDefinitionDetail_Mapper140733274 from __JVM_DefineClass__]
[Loaded ma.glasnost.orika.generated.Orika_OrderBase_CreateOrderReq_Mapper924640771 from __JVM_DefineClass__]
[Loaded ma.glasnost.orika.generated.Orika_OrderBaseServiceParameter_OrderBaseServiceParameterReq_Mapper886688266 from __JVM_DefineClass__]
[Loaded ma.glasnost.orika.generated.Orika_ServiceDefinitionParameter_ServiceDefinitionParameterReq_Mapper536925325 from __JVM_DefineClass__]
[Loaded ma.glasnost.orika.generated.Orika_ActivityDefinition_ActivityDefinitionReq_Mapper222008270 from __JVM_DefineClass__]
[Loaded ma.glasnost.orika.generated.Orika_ActivityDefinitionDetail_ActivityDefinitionDetail_Mapper337814190 from __JVM_DefineClass__]
[Loaded ma.glasnost.orika.generated.Orika_OrderBaseServiceParameter_OrderBaseServiceParameterReq_Mapper1104718977 from __JVM_DefineClass__]
[Loaded ma.glasnost.orika.generated.Orika_ServiceDefinitionParameter_ServiceDefinitionParameterReq_Mapper1058897791 from __JVM_DefineClass__]
[Loaded ma.glasnost.orika.generated.Orika_OrderLine_OrderlineReq_Mapper1088311475 from __JVM_DefineClass__]
[Loaded ma.glasnost.orika.generated.Orika_OrderLine_OrderlineReq_Mapper1251024503 from __JVM_DefineClass__]
[Loaded ma.glasnost.orika.generated.Orika_Reference_ReferenceReq_Mapper2103917791 from __JVM_DefineClass__]
[Loaded ma.glasnost.orika.generated.Orika_Reference_ReferenceReq_Mapper1328332983 from __JVM_DefineClass__]
[Loaded ma.glasnost.orika.generated.Orika_OrderBaseSubscribedServiceEntity_OrderBaseSubscribedServiceEntityReq_Mapper882926156 from __JVM_DefineClass__]
[Loaded ma.glasnost.orika.generated.Orika_OrderBaseSubscribedServiceEntity_OrderBaseSubscribedServiceEntityReq_Mapper1993002189 from __JVM_DefineClass__]
[Loaded ma.glasnost.orika.generated.Orika_OrderBase_CreateOrderReq_Mapper377652581 from __JVM_DefineClass__]
[Loaded ma.glasnost.orika.generated.Orika_ServiceDefinition_ServiceDefinitionReq_Mapper1617967640 from __JVM_DefineClass__]
[Loaded ma.glasnost.orika.generated.Orika_OrderLine_OrderlineReq_Mapper470033788 from __JVM_DefineClass__]
[Loaded ma.glasnost.orika.generated.Orika_Reference_ReferenceReq_Mapper1878357978 from __JVM_DefineClass__]
[Loaded ma.glasnost.orika.generated.Orika_OrderLine_OrderlineReq_Mapper404646422 from __JVM_DefineClass__]
[Loaded ma.glasnost.orika.generated.Orika_OrderBaseSubscribedServiceEntity_OrderBaseSubscribedServiceEntityReq_Mapper1959188152 from __JVM_DefineClass__]
[Loaded ma.glasnost.orika.generated.Orika_Reference_ReferenceReq_Mapper1509269350 from __JVM_DefineClass__]
[Loaded ma.glasnost.orika.generated.Orika_OrderBaseSubscribedServiceEntity_OrderBaseSubscribedServiceEntityReq_Mapper1716988231 from __JVM_DefineClass__]
[Loaded ma.glasnost.orika.generated.Orika_ServiceDefinition_ServiceDefinitionReq_Mapper1043329959 from __JVM_DefineClass__]
[Loaded ma.glasnost.orika.generated.Orika_ActivityDefinition_ActivityDefinitionReq_Mapper1062832431 from __JVM_DefineClass__]
被orika的 类加载信息刷屏了。orika是一个bean mapping的库,从输出日志来看,在做对象属性拷贝的时候会动态的生成并加载mapper类。来瞅瞅源码去
public Mapper<Object, Object> lookupMapper(MapperKey mapperKey) {
Object mapper = this.getRegisteredMapper(mapperKey.getAType(), mapperKey.getBType(), true); // 1
if(mapper == null && this.useAutoMapping) {
synchronized(this) {
try {
if(ClassUtil.isImmutable(mapperKey.getBType()) && !this.objectFactoryRegistry.containsKey(mapperKey.getBType())) {
throw new MappingException("No converter registered for conversion from " + mapperKey.getAType() + " to " + mapperKey.getBType() + ", nor any ObjectFactory which can generate " + mapperKey.getBType() + " from " + mapperKey.getAType());
}
if(LOGGER.isDebugEnabled()) {
LOGGER.debug("No mapper registered for " + mapperKey + ": attempting to generate");
}
ClassMap e = this.classMap(mapperKey.getAType(), mapperKey.getBType()).byDefault(new DefaultFieldMapper[0]).toClassMap();
this.buildObjectFactories(e);
mapper = this.buildMapper(e, true); // 2
this.initializeUsedMappers(e);
} catch (MappingException var5) {
var5.setSourceType(mapperKey.getAType());
var5.setDestinationType(mapperKey.getBType());
throw var5;
}
}
}
return (Mapper)mapper;
}
private GeneratedMapperBase buildMapper(ClassMap<?, ?> classMap, boolean isAutoGenerated) {
this.register(classMap.getAType(), classMap.getBType());
this.register(classMap.getBType(), classMap.getAType());
MapperKey mapperKey = new MapperKey(classMap.getAType(), classMap.getBType());
GeneratedMapperBase mapper = this.mapperGenerator.build(classMap);//3
mapper.setMapperFacade(this.mapperFacade);
mapper.setFromAutoMapping(isAutoGenerated);
if(classMap.getCustomizedMapper() != null) {
Mapper customizedMapper = classMap.getCustomizedMapper();
mapper.setCustomMapper(customizedMapper);
}
this.mappersRegistry.add(mapper);
this.classMapRegistry.put(mapperKey, classMap);
return mapper;
}
在this.mapperGenerator.build(classMap) 中 orika会生成A类型与B类型对象之间拷贝代码然后进行编译加载。
从1、2、3处可以看出,在做对象属性考被的时候,会动态生成Mapper类,并进行缓存。
那问题来了?不是有缓存的吗,怎么还在刷屏呢?移步看下隔壁小王写的业务代码。
private OrderBase convert(CreateOrderReq req) {
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();//就是他了
mapperFactory.classMap(CreateOrderReq.class, OrderBase.class).byDefault();
MapperFacade mapper = mapperFactory.getMapperFacade();
OrderBase rst = mapper.map(req, OrderBase.class);
return rst;
}
每次调用convert的时候,都会new一个新的DefaultMapperFactory(), 每个factory都持有它自己的mapper缓存。不断的创建factory,不断的动态创建mapper类进行加载。然后就没有然后了。
生成的Mapper 类是什么鬼?
package ma.glasnost.orika.generated;
public class Orika_UserVO_User_Mapper133221885 extends ma.glasnost.orika.impl.GeneratedMapperBase {
public void mapAtoB(java.lang.Object a, java.lang.Object b, ma.glasnost.orika.MappingContext mappingContext) {
super.mapAtoB(a, b, mappingContext);
com.xx.entity.security.User source = ((com.xx.entity.security.User) a);
com.xx.vo.security.UserVO destination = ((com.xx.vo.security.UserVO) b);
if (((java.lang.String) source.getPassword()) != null) {
destination.setPassword(((java.lang.String) source.getPassword()));
}
if (((java.lang.String) source.getUserName()) != null) {
destination.setUserName(((java.lang.String) source.getUserName()));
}
if (customMapper != null) {
customMapper.mapAtoB(source, destination, mappingContext);
}
}
public void mapBtoA(java.lang.Object a, java.lang.Object b, ma.glasnost.orika.MappingContext mappingContext) {
super.mapBtoA(a, b, mappingContext);
com.xx.vo.security.UserVO source = ((com.xx.vo.security.UserVO) a);
com.xx.entity.security.User destination = ((com.xx.entity.security.User) b);
if (((java.lang.String) source.getPassword()) != null) {
destination.setPassword(((java.lang.String) source.getPassword()));
}
if (((java.lang.String) source.getUserName()) != null) {
destination.setUserName(((java.lang.String) source.getUserName()));
}
if (customMapper != null) {
customMapper.mapBtoA(source, destination, mappingContext);
}
}
}
解决方案
使用OrikaBeanMapper类来进行拷贝额,OrikaBeanMapper 封装了单例的DefaultMapperFactory。