在启动时本地运行的应用程序(Spring,JPA Hibernate,Sybase 12,Webapp)在基于VisualVM的256MB堆空间中消耗40MB。当我触发返回70,000多行(文本数据无斑点)的搜索时,堆空间图的最大内存为256MB,并耗尽了内存。 我已经通过使用setMaxResults(limit)解决了这个问题。 但是,当我查询相同的数据,将其复制粘贴到文本文件并保存到文件系统时,可以看到该文本大小仅为26MB。
因此,实际上,通过从数据库加载26MB的文本来消耗216MB(从256-40开始), 谁 在内存 不足 时 消耗了190MB ?也许是框架,但是我看不出它可以消耗比实际加载的更多的数据…
__ 再次注意,我使用setMaxResults(limit)解决了这个问题,我的问题不是做什么,而是出于教育目的。
要考虑的一些事情:
您的操作系统可能使用每个字符编码8位的字符来存储文本文件。Java字符串内部全部以每个字符16位编码,是那里的空间的两倍。
只有几个数字的数字将被编码为文本,而不是数字。例如,“ 1”是文本文件中的一个字节字符,但值为1的long则是内存大小的八倍。
hibernate将从SQL结果集中获取值并将其映射到Java对象上,从而产生重复。它可能需要将结果集的内容包装/转换为您在映射上定义的类型。
如果您的每个实体的数据量实际上很小,并且具有大量实体,那么对象开销大小与数据大小的比率显然会很高。
如果集合中的数据很小,则集合的大小可以相对于数据快速增加。在一个极端的例子中,如果您有一个或两个字符串的LinkedList,则指针每实际数据的16-32位仅消耗192位。在数组列表中,指针指向16-32位数据仍将是64位。(当然假设是64位OS。)
您在hibernate状态下加载的每个对象都会被“跟踪”,以在所谓的L1缓存中进行脏检查。实际上,相对于大量具有少量数据的实体的数据大小而言,用于执行此操作的内部数据结构和工具可能存在相当大的开销。
-
因此,假设Java中的所有字符串,没有数字,没有日期,则26MB的数据已经是Java内存中的52MB数据,否则它将更大。
然后,如果将它分成许多小段,即700,000个小字符串而不是1,000个真正的长字符串,那么将数据结构开销的大小变成实际数据大小的三倍是完全合理的,轻松地使您超过200MB。