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

Ank*_*sal 9 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();

}
Run Code Online (Sandbox Code Playgroud)

和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
}
Run Code Online (Sandbox Code Playgroud)

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

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

在此输入图像描述

这是我的堆转储差异.

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

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

Gc/Memory Graph

在此输入图像描述

堆转储按大小排序

在此输入图像描述

Mad*_*apu 5

以下是我的观点:

因此,在GC运行的几个请求之后,内存会增加,它会释放内存,这将永远持续下去.

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

不一定是内存泄漏.但是你需要运行并点击应用程序更长的时间,看看在GC循环期间内存是如何释放的.只要内存使用类型遵循锯齿模式,它就是GC能够回收垃圾并且有效利用内存的一个指示器.

使用投影时,hibernate查询特定字段或从数据库中获取所有字段?

不能在投影的情况下仅获取指定的列.

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

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

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

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

最后,您可能需要通过java8 GC调优指南来了解GC并进行调优.


Ste*_*han 5

这看起来是正常行为.

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

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

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

该对象没有占用太多内存,每个查询的对象的567个实例占用了内存.

让我们来看看解释原因:

投影的每个实例都包含

  • a String未知长度(字符串不是基元,因此在纯字符数的顶部有大量元数据属性,但是只说1个字节)
  • a boolean分配1个字节
  • int每个字节2 个
  • a double2个字节
  • 并且LocalDateTime它是由几个字段的(以便让被看好,并以2个字节表示)

所以每个实例至少有 8个字节.每个查询最少567*8 = 4536字节.

你正在针对这个数据集发出6个查询4536*6 =每个方法调用27216个字节

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

这与你所观察到的相对接近,所以我认为什么都不是行为不端.

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