我正在使用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。 谁能告诉我什么是可能的问题?
我还分析了堆转储但无法得到任何东西?
这是我的堆转储差异。
我在一个请求和3个简单的普通mysql查询中发出6个hibernate查询(使用jdbc调用)
问题只是1个休眠呼叫。我认为我的休眠有些不对劲? 有什么方法可以进行基于请求的分析吗?
Gc / Memory Graph
按大小排序的堆转储
答案 0 :(得分:5)
以下是我的观点:
所以内存增加了,在GC运行的几个请求之后,它释放了内存,这将永远持续下去。
所以我的第一个问题是这个内存泄漏了吗?
不一定是内存泄漏。但是你需要运行并点击应用程序更长的时间,看看在GC循环期间内存是如何释放的。只要内存使用量kind of follows a sawtooth pattern,它就是GC能够回收垃圾并且内存被有效利用的一个指标。
使用投影时,hibernate会查询特定字段还是从数据库中提取所有字段?
否则在投影时仅取出指定的列。
然后我将此域映射到DTO,再次使用15-20MB内存?
不仅是您的DTO,而且hibernate
并且在spring-data-jpa
之上将在内部创建自己的对象以完成查询,并且这些对象可能正在等待GC。只要它们在GC循环后回收并且每个GC后内存使用量不会不断增加,这是一个健康的信号。
但是,除了每个请求使用的内存之外,您可能希望查看更大的图片,并且某些项目(不是详尽/完整列表)可能是:
最后,您可能希望通过java8 GC tuning guide了解GC并进行调整。
答案 1 :(得分:5)
这让我觉得这是正常行为。
所以我的第一个问题是这个内存泄漏了吗?
没有。内存泄漏要求内存在其使用寿命之外保持分配状态。由于GC正在清除查询消耗的总内存空间,因此您不会泄漏,只是使用内存。
但是我不明白这个对象如何能够获得如此高的记忆?
该对象占用的内存不多,它是占用内存的对象每个查询的567 实例。
让我们来看看解释原因:
投影的每个实例都包含
String
(字符串不是基元,因此有大量元数据属性在纯字符数的顶部,但只是说1个字节)boolean
,分配1个字节int
double
LocalDateTime
由多个字段组成(所以让我们保持乐观并说2个字节)因此每个实例至少 8个字节。 567 * 8 =每个查询最少4536字节。
您针对此数据集触发了6次查询 每个方法调用4536 * 6 = 27216字节
其中一些是hibernate的开销,它会在调用之间重用,所以你不会完全看到完整的理论足迹。
这与你所观察到的相近,所以我认为没有什么是行为不端。
如果您希望缩小占用空间,请重新评估重复使用尽可能多的数据的方法,以减少必须进行的查询次数。