在C和C ++中,atexit
函数在exit
内或main
返回后调用(理论上调用exit
:__libc_start_main(argc,argv) { __libc_constructors(); exit(main(argc,argv)); }
)。
有没有办法找出我们是否在退出序列中? C ++全局和局部静态的析构函数已在atexit
注册,因此您的代码在此阶段肯定可以调用。 (有趣的是,在某些平台上,如果你试图在exit
中创建一个C ++本地静态对象,它会在退出锁定时死锁!)
到目前为止,我最好的尝试如下:
static bool mainExited = false;
static void watchMain() {
static struct MainWatcher {
~MainWatcher() { mainExited = true; }
} watcher;
}
如果您想要退出,请拨打watchMain()
,mainExited
随时告诉您退出序列是否已经开始 - 当然,如果稍后初始化的本地 - 静态对象正在破坏!
可以改进技术来纠正这个问题吗,还是有其他方法可行?
虽然问题从语言的角度来看很有意思(有点像"我可以判断我是否在catch
块内?"),它&# 39; s对于概述用例也很有用。我在编写一些代码时遇到了这个问题,这些代码将在加载JVM和没有加载JVM的情况下运行(直接调用或通过JNI调用)。退出JVM后,将调用C atexit
处理程序,如果类加载器未卸载JNI共享库,则不会调用JNI_OnUnload
。
由于共享库的对象可以通过显式销毁(并且应该释放它们的资源)来破坏,并且在退出时通过清理,我需要安全地区分这两种情况,因为JVM已经消失了我们到达退出代码!基本上没有一点点嗅探,我无法在JNI规范/文档中找到共享库来了解JVM是否仍然存在,如果它已经消失,那么它就是&#39 ;尝试释放我们对Java对象的引用肯定是错误的。
答案 0 :(得分:7)
这里真正的问题是你列出的所有权语义搞砸了。 JVM有点拥有你的共享库,但也有点没有。您有一堆Java对象的引用,有时您需要清理,但有时您不需要清理。
这里的真正解决方案是不将Java对象的引用保持为全局变量。然后,无论出于何种原因卸载库,您都不需要知道JVM是否仍然存在。只需从Java引用的对象内部继续引用Java对象,然后让JVM关心它是否需要释放它们。
换句话说,首先不要让自己负责清理退出。
答案 1 :(得分:1)
您的观察者无需依赖任何静态初始化顺序:
#include <iostream>
struct MainWatcher // : boost::noncopyable
{
enum MainStatus { before, during, after };
MainWatcher(MainStatus &b): flag(b) { flag = during; }
~MainWatcher() { flag = after; }
MainStatus &flag;
};
//////////////////////////////////////////////////////////////////////
// Test suite
//////////////////////////////////////////////////////////////////////
// note: static data area is zero-initialized before static objects constructed
MainWatcher::MainStatus main_flag;
char const *main_word()
{
switch(main_flag)
{
case MainWatcher::before: return "before main()";
case MainWatcher::during: return "during main()";
case MainWatcher::after: return "after main()";
default: return "(error)";
}
}
struct Test
{
Test() { std::cout << "Test created " << main_word() << "\n"; }
~Test() { std::cout << "Test destroyed " << main_word() << "\n"; }
};
Test t1;
int main()
{
MainWatcher watcher(main_flag);
// rest of code
Test t2;
}