Java 从数据库加载26MB文本数据会消耗258MB的JVM堆

Java 从数据库加载26MB文本数据会消耗258MB的JVM堆,java,hibernate,jvm,out-of-memory,sybase,Java,Hibernate,Jvm,Out Of Memory,Sybase,基于VisualVM的应用程序(Spring、JPA Hibernate、Sybase 12、Webapp)在启动时本地运行时会消耗40MB的256MB堆空间。当我触发一个返回70000多行(文本数据没有blob)的搜索时,堆空间图将上升到256MB并抛出内存我已经通过使用setMaxResults(limit)解决了这个问题。但是,当我查询相同的数据,复制粘贴到文本文件并保存到文件系统时,我可以看到文本的大小只有26MB 因此,实际上,从数据库加载26MB的文本会消耗216MB(从256-40

基于VisualVM的应用程序(Spring、JPA Hibernate、Sybase 12、Webapp)在启动时本地运行时会消耗40MB的256MB堆空间。当我触发一个返回70000多行(文本数据没有blob)的搜索时,堆空间图将上升到256MB并抛出内存我已经通过使用setMaxResults(limit)解决了这个问题。但是,当我查询相同的数据,复制粘贴到文本文件并保存到文件系统时,我可以看到文本的大小只有26MB

因此,实际上,从数据库加载26MB的文本会消耗216MB(从256-40)的内存,内存不足时,谁在消耗190MB的内存?也许是框架,但我不认为它会消耗比实际加载的数据更多的数据


**请再次注意,我用setMaxResults(limit)解决了这个问题,我的问题不是做什么,而是为什么,出于教育目的。

需要考虑的一些事情:

您的操作系统可能使用每字符8位的编码来存储文本文件。Java字符串的内部编码都是每字符16位,空间是原来的两倍

只有几个数字的数字将被编码为比数字更小的文本。e、 例如,“1”在文本文件中是一个单字节字符,但值为1的长字符在内存中是该大小的八倍

hibernate会复制SQL结果集中的值,并将其映射到java对象。它可能需要将结果集的内容包装/转换为您在映射中定义的类型

如果每个实体的数据实际上很小,且实体数量很大,那么对象开销大小与数据大小的比率显然会很高

如果集合中有小块数据,则集合的大小可以相对于数据快速增加。在一个极端的例子中,如果您有一个或两个字符串的LinkedList,那么指针每16-32位实际数据就消耗192位。在数组列表中,指向16-32位数据的指针仍然是64位。(当然,假设是64位操作系统。)

在hibernate中加载的每个对象都会被“跟踪”,以便在所谓的一级缓存中进行脏检。对于具有少量数据的大量实体,与数据大小相比,用于执行此操作的内部数据结构和工具确实会有相当大的开销

--

因此,在java中,26MB的数据已经是52MB的内存数据,假设它都是字符串,没有数字,没有日期,否则它会更大

然后,如果它被分成许多小片段,700000个小字符串而不是1000个真正长的字符串,那么数据结构开销的大小是实际数据大小的三倍是完全合理的,可以很容易地将您推到200MB以上。

各种各样的事情

让我们考虑一下,您的行有10个文本列,这些文本列表示为具有10个字符串字段的简单java bean。

字符串有4个字段:一个char[]和3个int

字符串是对象的后代,对象有1个int和对其类的引用

在64位JVM上,这些引用很可能是8字节(但不一定,但为了参数起见,我们将坚持使用它)

一个10个字符的字符串将有一个char[10]和3个int,每个int是4个字节

char[10]是指向数组的指针。数组必须跟踪其长度,可能是另外4个字节,它也是一个对象(因此是类指针和另一个int)加上数据。但Java中的字符在内部表示为UTF-16,每个字符2字节。因此,10个字符的实际数组需要24个字节。对该数组的引用是指针

因此,单个字符串实例是:8+4表示对象,8+4+4+4表示字符串本身,8+4+20表示实际数据,即62字节

您的bean有10个字符串字段,加上扩展对象,所以是8+4+(10*8)

因此,对于100个字符的文本,数据库中的一行是8+4+(10*8)+(10*62),等于712个字节

这些都不是完美的数字,我无法具体说明数组是如何存储的,在64b JVM上,对象引用很可能不是8字节


但它会让你对所涉及的开销有一些了解。这只是为了你的原始数据。如果这些行存储在ArrayList中,那么,有70000*8只指向对象,560K只表示结构。

您的意思是190Mb用于表示数据,还是在读取数据的过程中分配了190Mb?我的意思是,当内存不足错误发生时,190Mb已全部用完。基于复制粘贴并将其保存到文本文件,数据库中的数据只有26MB。感谢您提供这一见解。