升级Spring Data MongoDB后调用MongoTemplate.save遇PermGen内存溢出求助
问题根源分析与解决方案
这不是Spring Data MongoDB的bug,而是你的POJO类字段命名不符合JavaBean规范,导致Spring Data的动态属性访问器重复生成类,最终撑爆了PermGen空间。
具体原因
Spring Data Commons中的ClassGeneratingPropertyAccessorFactory会为每个PersistentEntity生成一个动态Accessor类(比如你看到的LogRecord_Accessor_*),用来优化属性访问性能。但它是严格按照JavaBean规范来识别实体属性的:
- JavaBean规范要求字段名采用驼峰式(比如
eventId),对应的getter方法是getEventId(),setter是setEventId(...) - 你的
LogRecord类字段全是大写(比如EVENT_ID),getter方法是getEVENT_ID(),这不符合规范。Spring Data无法正确识别这些字段对应的属性,每次调用MongoTemplate.save()时,都会把当前对象当作一个"新的未识别实体",重复生成新的Accessor类。随着调用次数增加,PermGen里的动态类越来越多,最终触发OutOfMemoryError: PermGen space。
解决方案
方案1:修改POJO为符合JavaBean规范的命名(推荐)
把字段改成驼峰式,同时用@Field注解映射到MongoDB中原来的大写字段名,这样数据库结构不需要改动,代码也符合规范:
@Document(collection = "AuditLog") public class LogRecord { @Field("EVENT_ID") private long eventId; @Field("TIMESTAMP") private String timestamp; // 其他字段同理修改为驼峰式,并添加@Field注解映射原大写字段 // 对应的getter/setter也改成规范的驼峰式 public long getEventId() { return eventId; } public void setEventId(long eventId) { this.eventId = eventId; } // 其他getter/setter同理修改 }
这样Spring Data能正确识别实体属性,只会生成一次Accessor类,不会重复创建,PermGen溢出问题就会解决。
方案2:切换为反射式属性访问器(不推荐,性能稍差)
如果暂时无法修改POJO,可以关闭动态类生成,改用反射式的属性访问器。你可以通过配置AbstractMappingContext的persistentPropertyAccessorFactory来实现:
@Configuration public class MongoConfig extends AbstractMongoConfiguration { // 其他MongoDB配置... @Override protected MappingMongoConverter mappingMongoConverter() throws Exception { MappingMongoConverter converter = super.mappingMongoConverter(); // 切换为反射式属性访问器 ((MongoMappingContext) converter.getMappingContext()) .setPersistentPropertyAccessorFactory(ReflectionPropertyAccessorFactory.getInstance()); return converter; } }
这种方式会用反射来访问属性,不会生成动态类,避免PermGen溢出,但性能会比动态生成的Accessor稍差。
验证
修改后重启服务,观察YourKit中的类数量,LogRecord_Accessor_*类只会存在一个(或者不再生成),PermGen空间也不会持续增长,问题即可解决。
内容的提问来源于stack exchange,提问作者Bluesky
相关产品推荐
相关产品推荐

