记一次PERM区内存泄漏

现象

生产上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 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。

你可能感兴趣的:(记一次PERM区内存泄漏)