Java请求占用40-50MB内存(Spring JPA Hibernate)

时间:2018-03-30 12:42:21

标签: java spring hibernate jpa

我正在使用JPA Hibernate的spring boot。 我正在监视Heap的服务,发现我的每个请求大约需要40-50 MB。

所以内存增加了,在GC运行的几个请求之后,它释放了内存,这将永远持续下去。

所以我的第一个问题是这个内存泄漏了吗?

此外,我正在努力找到造成这种情况的原因。所以,我使用Runtime.getRuntime()freeMemory和totalMemory()来识别在获得一个db调用并用它填充投影时大约使用了15MB

public interface RecommendationProjection {
    public String getType();
    public boolean getIsOK();
    public int getId();
    public int getTagCount();
    public double getQuality() ;
    public LocalDateTime getLastActivity();

}

并且hibernate返回567条记录,所以基本上我从DB获得的是567以上投影的列表,但是我不明白这个对象如何能够获得如此高的内存?休眠导致这个吗?

使用投影时,hibernate会查询特定字段还是从数据库中提取所有字段?

然后我将此域映射到DTO,再次使用15-20MB内存? 这是我的DTO

public class RecommendationInfoDTO {
    private String type;
    private boolean isOK;
    private int id;
    private int tagCount;
    private double quality ;
    @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss", timezone="IST")
    private LocalDateTime lastActivity;


    .. getters and setters
}

仅供参考:对于监控,我使用的是VisualVM。 谁能告诉我什么是可能的问题?

我还分析了堆转储但无法得到任何东西?

enter image description here

这是我的堆转储差异。

我在一个请求和3个简单的普通mysql查询中发出6个hibernate查询(使用jdbc调用)

问题只是1个休眠呼叫。我认为我的休眠有些不对劲? 有什么方法可以进行基于请求的分析吗?

Gc / Memory Graph

enter image description here

按大小排序的堆转储

enter image description here

2 个答案:

答案 0 :(得分:5)

以下是我的观点:

  

所以内存增加了,在GC运行的几个请求之后,它释放了内存,这将永远持续下去。

     

所以我的第一个问题是这个内存泄漏了吗?

不一定是内存泄漏。但是你需要运行并点击应用程序更长的时间,看看在GC循环期间内存是如何释放的。只要内存使用量kind of follows a sawtooth pattern,它就是GC能够回收垃圾并且内存被有效利用的一个指标。

  

使用投影时,hibernate会查询特定字段还是从数据库中提取所有字段?

否则在投影时仅取出指定的列。

  

然后我将此域映射到DTO,再次使用15-20MB内存?

不仅是您的DTO,而且hibernate并且在spring-data-jpa之上将在内部创建自己的对象以完成查询,并且这些对象可能正在等待GC。只要它们在GC循环后回收并且每个GC后内存使用量不会不断增加,这是一个健康的信号。

但是,除了每个请求使用的内存之外,您可能希望查看更大的图片,并且某些项目(不是详尽/完整列表)可能是:

  1. 了解正常负载和峰值负载情况下内存使用情况的一段时间。
  2. 次要和主要GC的发生频率及其对应用程序的影响?
  3. 应用程序是否在GC本身花费了太多时间"?
  4. 根据应用程序行为调整GC。例如:应用程序是否为服务请求等创建了太多短期对象。
  5. 鉴于堆/ GC配置,应用程序是否能够按预期满足您的应用程序响应时间和吞吐量?
  6. 最后,您可能希望通过java8 GC tuning guide了解GC并进行调整。

答案 1 :(得分:5)

这让我觉得这是正常行为。

  

所以我的第一个问题是这个内存泄漏了吗?

没有。内存泄漏要求内存在其使用寿命之外保持分配状态。由于GC正在清除查询消耗的总内存空间,因此您不会泄漏,只是使用内存。

  

但是我不明白这个对象如何能够获得如此高的记忆?

该对象占用的内存不多,它是占用内存的对象每个查询的567 实例

让我们来看看解释原因:

投影的每个实例都包含

  • 一个未知长度的String(字符串不是基元,因此有大量元数据属性在纯字符数的顶部,但只是说1个字节)
  • a boolean,分配1个字节
  • 每个字节2 int
  • 一个2个字节的double
  • LocalDateTime由多个字段组成(所以让我们保持乐观并说2个字节)

因此每个实例至少 8个字节。 567 * 8 =每个查询最少4536字节。

您针对此数据集触发了6次查询 每个方法调用4536 * 6 = 27216字节

其中一些是hibernate的开销,它会在调用之间重用,所以你不会完全看到完整的理论足迹。

这与你所观察到的相近,所以我认为没有什么是行为不端。

如果您希望缩小占用空间,请重新评估重复使用尽可能多的数据的方法,以减少必须进行的查询次数。