我正在开发Android NDK上的C ++游戏(android-ndk-r9b)。如果我写这个:
class Test
{
char c[1024*1024*1024];
};
//in main
try {
Test* p;
while (1) {
p = new Test();
}
} catch (bad_alloc) {
cout << "bad_alloc\n";
}
它不会抛出。如果我试试这个:
void no_memory_by_new() {
cout << "no_memory_by_new\n";
throw bad_alloc();
}
//in main
set_new_handler(no_memory_by_new);
Test* p;
while (1) {
p = new Test();
}
它也不起作用。最后,如果我试试这个:
//in main
set_new_handler(no_memory_by_new);
int* p;
while (1) {
p = new int[1024*1024*1024];
}
然后调用no_memory_by_new
。我真的很困惑。任何人都可以帮助我吗?
答案 0 :(得分:3)
这是GNU libstdc ++的Android构建中的错误。如果您查看 operator new
implementation,如果_GLIBCXX_THROW_OR_ABORT
返回malloc
,您会看到它NULL
。接下来,如果您查看_GLIBCXX_THROW_OR_ABORT
的{{3}},只有__EXCEPTIONS
定义时,您才会看到它抛出bad_alloc;否则,只需拨打abort
。由于某种原因,__EXCEPTIONS
宏未在GNU libstdc ++的Android版本中定义,因此它调用abort
- 正如您在案例中看到的那样。
我已经使用Android NDK r10d和CrystaX NDK 10.1检查了这种行为 - 在两种情况下它都是相同的。我已经在CrystaX NDK中提交了definition来解决这个问题。要在Google的NDK中修复此问题,您还应该在ticket
中提交票证
__EXCEPTIONS
,因此如果operator new
返回bad_alloc
,malloc
实际上会抛出NULL
。问题实际上在你的代码中,但要弄清楚它有点棘手。见下面的解释。
TL; DR:operator new
返回指向&#34;已分配&#34;的指针内存(因此从它的角度来看,没有理由抛出std::bad_alloc
),但首次访问该内存会导致崩溃,因为实际上这些页面不可用。
更详细的解释:
以下是我用于测试的完整代码:
#include <new>
#include <stdio.h>
#include <string.h>
class Test
{
public:
Test() {
::fprintf(stderr, "ctor start\n");
//memset(c, 0, sizeof(c));
::fprintf(stderr, "ctor finish\n");
}
private:
char c[1024*1024*1024];
};
int main()
{
try {
while (1) {
Test *p = new Test();
if (!p)
return 1;
}
return 1;
} catch (std::bad_alloc) {
return 0;
}
}
如果您编译此测试并在设备上运行它,您将在某次迭代中得到std::bad_alloc
(我在第三次迭代时得到它)。但是如果你在Test的构造函数中取消注释memset
,应用程序将在第一次memset
调用时崩溃。如果完全删除Test的构造函数,它也会崩溃 - 只是因为在这种情况下编译器将生成构造函数,它对所有成员进行零初始化 - 即与我们对memset
相同。
这里的区别在于malloc
(在operator new
内部使用)返回指向&#34;已分配&#34;的指针。内存,但实际它没有分配;这个区域只是标记为&#34;将来需要分配,当应用程序实际引用它时#34;。出于性能原因,这就是Linux内核处理它的方式。下一步,当你(或编译器)用零填充数组时,应用程序实际访问那些页面,但不幸的是,系统中没有空闲内存,因此Linux内核调用OOM杀手,结果导致进程终止。 / p>
这不是特定于Android的。事实上,GNU / Linux系统也是如此;唯一的区别是系统可用的内存量(在Android上它远远低于在服务器上,显而易见的原因)。
答案 1 :(得分:0)
为了获得工作异常,您需要使用其他C ++标准库之一进行构建,而不是默认库。添加例如APP_STL := gnustl_static
到jni/Application.mk
。此外,您需要启用例外,将LOCAL_CPP_FEATURES += exceptions
添加到Android.mk
,或将APP_CPPFLAGS += -fexceptions
添加到jni/Application.mk
。