Java 每次迭代后,JPA开始消耗越来越多的内存
目前,我试图在JPA的帮助下存储一些来自web api的新闻。 我需要存储3个实体:网页、新闻发布和返回新闻发布的查询。我三个人各有一张桌子。我的简化JPA实体如下所示:Java 每次迭代后,JPA开始消耗越来越多的内存,java,jpa,spring-data-jpa,Java,Jpa,Spring Data Jpa,目前,我试图在JPA的帮助下存储一些来自web api的新闻。 我需要存储3个实体:网页、新闻发布和返回新闻发布的查询。我三个人各有一张桌子。我的简化JPA实体如下所示: @Entity @Data @Table(name = "NewsPosts", schema = "data") @EqualsAndHashCode @NoArgsConstructor @AllArgsConstructor @Builder public class NewsPo
@Entity
@Data
@Table(name = "NewsPosts", schema = "data")
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class NewsPost {
@Id
@Column(name = "id")
private long id;
@Basic
@Column(name = "subject")
private String subject;
@Basic
@Column(name = "post_text")
private String postText;
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
@JoinColumn(name = "newsSite")
private NewsSite site;
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.MERGE)
@JoinTable(name = "query_news_post", joinColumns = @JoinColumn(name = "newsid"), inverseJoinColumns = @JoinColumn(name = "queryid"))
private Set<QueryEntity> queries;
}
@Entity
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "queries", schema = "data")
@EqualsAndHashCode
public class QueryEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private int id;
@EqualsAndHashCode.Exclude
@Basic
@Column(name = "query")
private String query;
// needs to be exclueded otherwise we can create stack overflow, because of circular references...
@EqualsAndHashCode.Exclude
@ToString.Exclude
@ManyToMany(mappedBy = "queries", fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
Set<PostsEntity> posts;
}
@Entity
@Data
@Table(name = "sites", schema = "data")
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class newsSite {
@Id
@Column(name = "SiteId")
private long id;
@Basic
@Column(name = "SiteName")
private String site;
}
要更新帖子,我将按如下方式进行操作:
private void updatePosts(List<NewsPost> posts){
posts.forEach(post->{
NewsPost foundPost = postRepo.getById(post.getId());
if(foundPost!=null){
post.getQueries().addAll(foundPost.getQueries());
}});
}
我很确定这是写作的过程。如果我保持软件的逻辑不变,但只跳过合并,或者只是打印实体或将实体转储到文件中,那么一切都可以快速工作,不会出现错误,因此合并注释似乎有问题
关于我的程序是否因为内存消耗而死亡的问题,这取决于它。如果我在我的mac上运行它,它会消耗8+千兆字节的ram,但mac OS会处理这个问题,并将ram交换到磁盘。如果我将其作为docker容器von CentOS运行,由于内存不足,进程将被终止
现在不知道这是否相关,但我使用的是OpenJDK 11、Springboot 2.2.6和MYSQL 8数据库
我在application.yml中对jpa进行了如下配置:
spring:
main:
allow-bean-definition-overriding: true
datasource:
url: "jdbc:mysql://db"
username: user
password: secret
driver-class-name: com.mysql.cj.jdbc.Driver
test-while-idle: true
validation-query: Select 1
jpa:
database-platform: org.hibernate.dialect.MySQL8Dialect
hibernate:
ddl-auto: none
properties:
hibernate:
event:
merge:
entity_copy_observer: allow
```
许多人渴望的关系会带来许多目标。关于LAZY realtion,请确保获取它们,因为如果不获取它们,则通过完整的对象将其转换为JSON或POJO将为每个未使用获取初始化的对象抛出一个查询,这是一件危险的事情。如果不需要所有这些,可以使用@JsonIgnore注释。如果合并过程有问题,可以通过添加
em.flush()快速修复entityManager
中的内存消耗代码>和em.clear()代码>每次合并后:
EntityTransaction transaction = em.getTransaction();
transaction.begin();
entities.forEach(entity-> {
em.merge(entity);
em.flush();
em.clear();
});
transaction.commit();
然而,我认为你应该改变你的模式。加载每个帖子的所有现有查询只是为了添加新的查询是非常低效的。您可以将N-M关系建模为一个新实体,只需保留新关系。我自己尝试解决了这个问题。我为多对多关系创建了一个实体。之后,我为每个实体创建了CRUD存储库,并使用CRUD存储库中的saveAll
。这在内存方面也很好。GC现在在内存可视化中生成预期的链锯模式。但是我仍然不知道为什么我之前在注释中创建的多对多关系与连接表产生了有关内存管理的问题。有人能解释一下为什么这解决了我的问题,因为很多人都在创建循环依赖关系吗?但据我所知,GC也会发现循环依赖项。您能否发布执行所有“爬网”和保存操作的实际代码?请确保在发布的代码中包含事务边界。还要确保您了解JPA实体的生命周期。如果你不这样做,这很可能是你的问题的原因。它是否真的耗尽了内存?或者它只是消耗了更多内存,但在其他方面运行良好?谢谢大家的评论。我试图添加所需的其他信息。您的模型有点难以理解:您在开始时提到了您的三个实体。您对三个实体的实现通过链接到其他一些实体。忽略拼写错误,我发现NewsPost
,newsite
,QueryEntity
,SidSocialgistQueryEntity
和socialgistboardpossentity
。你能描述一下你的模型或者改正一下吗?但是我怎样才能更新它呢?不确定您是否得到了这个问题?我完全确定这个问题,例如,如果在QueryEntity类中,您以双向方式将与newsPost类的关系定义为“渴望”,那么您将创建对象的周期性初始化,不会花费很长时间使内存溢出。关于对象更新,无论是在“渴望”还是“懒惰”中,都可以用相同的方式进行更新,只要您之前进行了查找(您这样做了),并且没有退出事务。您是否尝试使用@JsonIgnore注释在将实体映射到平面对象时中断双向初始化?
spring:
main:
allow-bean-definition-overriding: true
datasource:
url: "jdbc:mysql://db"
username: user
password: secret
driver-class-name: com.mysql.cj.jdbc.Driver
test-while-idle: true
validation-query: Select 1
jpa:
database-platform: org.hibernate.dialect.MySQL8Dialect
hibernate:
ddl-auto: none
properties:
hibernate:
event:
merge:
entity_copy_observer: allow
```
EntityTransaction transaction = em.getTransaction();
transaction.begin();
entities.forEach(entity-> {
em.merge(entity);
em.flush();
em.clear();
});
transaction.commit();