我有一个简单的问题。
byte[] responseData = ...;
String str = new String(responseData);
String withKey = "{\"Abcd\":" + str + "}";
在上面的代码中,这三行是3X内存。例如,如果responseData是1mb,那么第2行将在内存中占用额外的1mb,然后第3行将需要额外的1mb + xx。这是真的?如果没有,那么它将如何运作。如果是,那么解决这个问题的最佳方法是什么。 StringBuffer会在这里帮忙吗?
答案 0 :(得分:4)
是的,这听起来是正确的。可能更多是因为您的1MB字节数组需要转换为UTF-16,因此根据编码,它可能更大(如果输入是ASCII,则为2MB)。
请注意,一旦使用它的变量超出范围,垃圾收集器就可以回收内存。您可以尽早将它们设置为null
,以帮助它尽可能及时地进行此操作(例如,在构造String之后responseData = null;
)。
如果是,那么解决此问题的最佳方法是什么
“修复”意味着一个问题。如果你有足够的内存,没有问题。
问题是我得到OutOfMemoryException,因为来自服务器的byte []数据非常大,
如果不这样做,你必须考虑在内存中保留1MB字符串的更好选择。也许你可以从文件中流出数据?或者直接处理字节数组?这是什么类型的数据?
答案 1 :(得分:1)
问题是我得到
OutOfMemoryException
,因为来自服务器的byte[]
数据非常大,这就是为什么我需要首先弄清楚我做错了什么...... / p>
是。基本上你的基本问题是你试图一次将整个字符串保存在内存中。对于足够大的字符串,总是会失败...即使您以最佳内存效率方式对其进行编码也是如此。 (这本身就很复杂。)
最终解决方案(即“缩放”的解决方案)是执行以下操作之一:
将数据流式传输到文件系统,或
以一种不需要表示整个“字符串”的方式处理它。
您问StringBuffer
是否有帮助。它可能会有所帮助...只要你正确使用它。诀窍是确保预分配 StringBuffer
(实际上StringBuilder
更好!!)足够大以容纳所需的所有字符。然后使用字符集解码器(直接或使用Reader管道)将数据复制到其中。
但即使使用最佳编码,您的输入byte[]
也可能需要3倍的峰值。
请注意,您的OOME问题可能与GC或存储泄漏无关。它实际上是关于您正在使用的数据类型的基本空间要求......以及Java不提供“字节串”数据类型的事实。
答案 2 :(得分:0)
我的apidocs中没有这样的OutOfMemoryException
。如果它是OutOfMemoryError
,特别是在服务器端,你肯定遇到了问题。
当您收到来自客户的big
个请求时,那些String
相关的语句不是第一个问题。将3X减少到1X不是解决方案。
对不起,如果没有任何进一步的代码,我会情不自禁。
使用后端存储
您不应将整个请求正文存储在byte[]
上。您可以将它们直接存储在任何后端存储上,例如本地文件,远程数据库或云存储。
我会
copy stream from request to back-end with small chunked buffer
使用流
如果可以使用Streams而不是对象。
我会
response.getWriter().write("{\"Abcd\":");
copy <your back-end stored data as stream>);
response.getWriter().write("}");
答案 3 :(得分:0)
是的,如果你使用Stringbuffer作为你拥有的代码,你将在最后一步节省1mb的堆空间。但是,考虑到您拥有的数据大小,我建议使用外部存储器算法,您只需将部分数据存储到内存中,处理它并将其重新存储。
答案 4 :(得分:0)
正如其他人所说,你应该尝试不在你的移动应用中拥有如此大的对象,并且流媒体应该是你最好的解决方案。
也就是说,有一些技术可以减少你的应用现在使用的内存量:
byte[] responseData
,因此可以尽快释放它使用的内存(假设它没有在其他任何地方使用)首先创建最大的String,然后substring()
,Android使用Apache Harmony进行标准的Java库实现。如果选中its String class implementation,您将看到substring()
只是通过创建一个新的String对象来实现,该对象具有对原始数据的正确开始和结束偏移量,并且不会创建重复副本。因此,执行以下操作会使整体内存消耗减少至少1/3 :
String withKey = StringBuilder()。append(“{\”Abcd \“)。append(str).append(”}“)。toString(); String str = withKey.substring(“{\”Abcd \“。length(),withKey.length() - ”}“。length());
永远不要使用像"{\"Abcd\":" + str + "}"
这样的大字符串,在引擎盖下“string_a”+“string_b”被实现为新的StringBuilder().append("string_a").append("string_b").toString();
所以隐含地你创建了两个(或至少一个)如果编译器是mart)StringBuilders。对于大型字符串,最好自己接管这个过程,因为你对编译器没有的程序有深入的领域知识,并且知道如何最好地操作字符串。