我正在开发一个需要与 Android 2.3 (Gingerbread) 兼容的应用程序,我用于开发测试的设备是运行 Android 2.3.6 的 Motorola Atrix MB860。
在此设备中,我获得了大约 40MB 的最大堆空间,据我所知,我的应用程序使用了大约 33MB,但无论如何我都会遇到 OutOfMemoryError
异常。
基本上,我的代码中与此问题相关的部分创建了一个大的String
(8MB - 我知道它相当大,但如果它太小,则无法满足其中一个要求)然后继续创建2个线程,使用该字符串并发写入某个内存空间。
这是代码:
// Create random string
StringBuilder sb = new StringBuilder();
sb.ensureCapacity(8388608); // ensuring 8 MB is allocated on the heap for the StringBuilder object
for (long i = 0; i < DATA_SIZE; i++) {
char c = chars[new Random().nextInt(chars.length)];
sb.append(c);
}
String randomByteString = sb.toString();
ExecutorService executor = Executors.newFixedThreadPool(2);
for (int i = 0; i < 2; i++) {
Runnable worker = new SlidingBubbles(param1, param2, randomByteString)
executor.execute(worker);
}
// This will make the executor accept no new threads
// and finish all existing threads in the queue
executor.shutdown();
// Wait until all threads are finish
while(!executor.isTerminated()) {
// wait for bubble threads to finish working...
}
以及线程的例程:
private class SlidingBubbles implements Runnable {
private int param1, param2;
private String randomByteString;
private final Object mSignal = new Object();
private volatile long tempBytesWritten = 0;
private volatile long totalBytesWritten = 0;
public SlidingBubbles(int param1, int param2, String randomByteString) {
this.param1= param1;
this.param2= param2;
this.randomByteString = randomByteString;
}
private void doIt() {
File file = null;
RandomAccessFile randomAccessFile = null;
FileChannel fc = null;
try {
while(param1> 0) {
// Instantiate the 1st bubble file
file = new File(TARGET_DIR, String.valueOf(Calendar.getInstance().getTimeInMillis()));
while(param2 > 0) {
randomAccessFile = new RandomAccessFile(file, "rwd");
fc = randomAccessFile.getChannel();
fc.position(fc.size());
synchronized (mSignal) {
tempBytesWritten = fc.write(ByteBuffer.wrap(randomByteString.getBytes()));
totalBytesWritten += tempBytesWritten;
}
// some other things that don't matter
}
@Override
public void run() {
wipe();
}
}
对我来说,尴尬的是,在线程例程的第 30 行 (tempBytesWritten = fc.write(ByteBuffer.wrap(randomByteString.getBytes()));
),第二个线程 ("pool-1-thread-2”)启动异常,退出,第一个线程(“pool-1-thread-1”)继续(实际上,开始)正常执行。
当 JVM 完成为该大 String
分配空间时,应用程序正在使用 33MB 的堆。正如您从代码中看到的,String
仅创建一次,但随后在两个线程中多次使用。
线程不应该只使用对String
的引用而不是复制它吗? (在这种情况下,这将超过 40MB 的限额)。
我还必须指出,增加 Gingerbread ( previous research ) 上的堆空间是不可能的(或者至少看起来是,据我的理解)。
我有什么遗漏的吗? 非常感谢任何帮助。
最佳答案
您可以静态拥有 8MB 数据一次,并且永远不会创建它的副本。在 Android 上,StringBuilder
与 String
共享内部 char[]
,但 String#getBytes()
创建一个副本每次的数据。
我假设您的字符是纯 ASCII,当它们更特殊时,这无法正常工作。
Random random = new Random(); // once!
byte[] data = new byte[8388608];
for (int i = 0; i < data.length; i++) {
data[i] = (byte) chars[random.nextInt(chars.length)];
}
上面将创建一次数据而无需复制。另请注意 new Random()
8388608?循环中的次数也会导致大量内存使用,但它们应该很快被垃圾收集。
当你这样做时
public SlidingBubbles(int param1, int param2, byte[] data) {
...
synchronized (mSignal) {
tempBytesWritten = fc.write(ByteBuffer.wrap(data));
您不再创建该数据的副本,ByteBuffer.wrap
不会创建该数据的副本。无论您做什么,请将完成的 byte[]
传递给 SlidingBubbles
。
P.s: while(!executor.isTermminate()) {
是错误的方法,有一个方法:How to wait for all threads to finish, using ExecutorService?
关于java - 多线程时出现 OutOfMemory 异常...堆空间?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27194708/