如何处理 :java.util.concurrent.TimeoutException: android.os.BinderProxy.finalize() 在 10 秒错误后超时?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24021609/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to handle :java.util.concurrent.TimeoutException: android.os.BinderProxy.finalize() timed out after 10 seconds errors?
提问by emmby
We're seeing a number of TimeoutExceptions
in GcWatcher.finalize, BinderProxy.finalize
, and PlainSocketImpl.finalize
. 90+% of them happen on Android 4.3. We're getting reports of this from Crittercism from users out in the field.
我们看到了许多TimeoutExceptions
inGcWatcher.finalize, BinderProxy.finalize
和PlainSocketImpl.finalize
。其中 90+% 发生在 Android 4.3 上。我们从 Crittercism 收到来自现场用户的报告。
The error is a variation of: "com.android.internal.BinderInternal$GcWatcher.finalize() timed out after 10 seconds
"
错误是以下内容的变体:“ com.android.internal.BinderInternal$GcWatcher.finalize() timed out after 10 seconds
”
java.util.concurrent.TimeoutException: android.os.BinderProxy.finalize() timed out after 10 seconds
at android.os.BinderProxy.destroy(Native Method)
at android.os.BinderProxy.finalize(Binder.java:459)
at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:187)
at java.lang.Daemons$FinalizerDaemon.run(Daemons.java:170)
at java.lang.Thread.run(Thread.java:841)
So far we haven't had any luck reproducing the problem in house or figuring out what might have caused it.
到目前为止,我们还没有在内部重现该问题或找出可能导致该问题的原因。
Any ideas what can cause this? Any idea how to debug this and find out which part of the app causes this? Anything that sheds light on the issue helps.
有什么想法会导致这种情况吗?知道如何调试它并找出应用程序的哪个部分导致这种情况吗?任何能阐明这个问题的东西都会有所帮助。
More Stacktraces:
更多堆栈跟踪:
1 android.os.BinderProxy.destroy
2 android.os.BinderProxy.finalize Binder.java, line 482
3 java.lang.Daemons$FinalizerDaemon.doFinalize Daemons.java, line 187
4 java.lang.Daemons$FinalizerDaemon.run Daemons.java, line 170
5 java.lang.Thread.run Thread.java, line 841
2
2
1 java.lang.Object.wait
2 java.lang.Object.wait Object.java, line 401
3 java.lang.ref.ReferenceQueue.remove ReferenceQueue.java, line 102
4 java.lang.ref.ReferenceQueue.remove ReferenceQueue.java, line 73
5 java.lang.Daemons$FinalizerDaemon.run Daemons.java, line 170
6 java.lang.Thread.run
3
3
1 java.util.HashMap.newKeyIterator HashMap.java, line 907
2 java.util.HashMap$KeySet.iterator HashMap.java, line 913
3 java.util.HashSet.iterator HashSet.java, line 161
4 java.util.concurrent.ThreadPoolExecutor.interruptIdleWorkers ThreadPoolExecutor.java, line 755
5 java.util.concurrent.ThreadPoolExecutor.interruptIdleWorkers ThreadPoolExecutor.java, line 778
6 java.util.concurrent.ThreadPoolExecutor.shutdown ThreadPoolExecutor.java, line 1357
7 java.util.concurrent.ThreadPoolExecutor.finalize ThreadPoolExecutor.java, line 1443
8 java.lang.Daemons$FinalizerDaemon.doFinalize Daemons.java, line 187
9 java.lang.Daemons$FinalizerDaemon.run Daemons.java, line 170
10 java.lang.Thread.run
4
4
1 com.android.internal.os.BinderInternal$GcWatcher.finalize BinderInternal.java, line 47
2 java.lang.Daemons$FinalizerDaemon.doFinalize Daemons.java, line 187
3 java.lang.Daemons$FinalizerDaemon.run Daemons.java, line 170
4 java.lang.Thread.run
回答by oba
Full disclosure- I'm the author of the previously mentioned talk in TLV DroidCon.
完全披露- 我是前面提到的 TLV DroidCon 演讲的作者。
I had a chance to examine this issue across many Android applications, and discuss it with other developers who encountered it - and we all got to the same point: this issue cannot be avoided, only minimized.
我有机会在许多 Android 应用程序中检查这个问题,并与遇到它的其他开发人员讨论 - 我们都得到了同一点:这个问题无法避免,只能最小化。
I took a closer look at the default implementation of the Android Garbage collector code, to understand better why this exception is thrown and on what could be the possible causes. I even found a possible root cause during experimentation.
我仔细查看了 Android 垃圾收集器代码的默认实现,以更好地了解抛出此异常的原因以及可能的原因。我什至在实验过程中发现了一个可能的根本原因。
The root of the problem is at the point a device "Goes to Sleep" for a while - this means that the OS has decided to lower the battery consumption by stopping most User Land processes for a while, and turning Screen off, reducing CPU cycles, etc. The way this is done - is on a Linux system level where the processes are Paused mid run. This can happen at any time during normal Application execution, but it will stop at a Native system call, as the context switching is done on the kernel level. So - this is where the Dalvik GC joins the story.
问题的根源在于设备“进入睡眠”一段时间 - 这意味着操作系统已决定通过暂时停止大多数 User Land 进程并关闭 Screen 来降低电池消耗,从而减少 CPU 周期等。这样做的方式 - 在 Linux 系统级别上,其中进程在运行中暂停。这可能在正常应用程序执行期间的任何时间发生,但它会在本地系统调用处停止,因为上下文切换是在内核级别完成的。所以 - 这就是 Dalvik GC 加入故事的地方。
The Dalvik GC code (as implemented in the Dalvik project in the AOSP site) is not a complicated piece of code. The basic way it work is covered in my DroidCon slides. What I did not cover is the basic GC loop - at the point where the collector has a list of Objects to finalize (and destroy). The loop logic at the base can be simplified like this:
Dalvik GC 代码(在 AOSP 站点的 Dalvik 项目中实现)并不是一段复杂的代码。它的基本工作方式在我的 DroidCon 幻灯片中有介绍。我没有涉及的是基本的 GC 循环——在这个点上,收集器有一个要完成(和销毁)的对象列表。底部的循环逻辑可以这样简化:
- take
starting_timestamp
, - remove object for list of objects to release,
- release object -
finalize()
and call nativedestroy()
if required, - take
end_timestamp
, - calculate (
end_timestamp - starting_timestamp
) and compare against a hard coded timeout value of 10 seconds, - if timeout has reached - throw the
java.util.concurrent.TimeoutException
and kill the process.
- 取
starting_timestamp
, - 删除对象以获取要释放的对象列表,
- 释放对象 -
finalize()
并destroy()
在需要时调用本机, - 取
end_timestamp
, - 计算 (
end_timestamp - starting_timestamp
) 并与 10 秒的硬编码超时值进行比较, - 如果超时 - 抛出
java.util.concurrent.TimeoutException
并终止进程。
Now consider the following scenario:
现在考虑以下场景:
Application runs along doing its thing.
应用程序一直在做它的事情。
This is not a user facing application, it runs in the background.
这不是面向用户的应用程序,它在后台运行。
During this background operation, objects are created, used and need to be collected to release memory.
在此后台操作期间,对象被创建、使用并需要被收集以释放内存。
Application does not bother with a WakeLock - as this will affect the battery adversely, and seems unnecessary.
应用程序不会打扰 WakeLock - 因为这会对电池产生不利影响,而且似乎没有必要。
This means the Application will invoke the GC from time to time.
这意味着应用程序将不时调用 GC。
Normally the GC runs is completed without a hitch.
通常,GC 运行会顺利完成。
Sometimes (very rarely) the system will decide to sleep in the middle of the GC run.
有时(很少)系统会决定在 GC 运行过程中休眠。
This will happen if you run your application long enough, and monitor the Dalvik memory logs closely.
如果您运行应用程序的时间足够长,并密切监视 Dalvik 内存日志,就会发生这种情况。
Now - consider the timestamp logic of the basic GC loop - it is possible for the device to start the run, take a start_stamp
, and go to sleep at the destroy()
native call on a system object.
现在 - 考虑基本 GC 循环的时间戳逻辑 - 设备可以开始运行,执行start_stamp
,然后destroy()
在系统对象的本机调用时进入睡眠状态。
When it wakes up and resumes the run, the destroy()
will finish, and the next end_stamp
will be the time it took the destroy()
call + the sleep time.
当它醒来并继续运行时,destroy()
将完成,接下来end_stamp
将是destroy()
调用时间+睡眠时间。
If the sleep time was long (more than 10 seconds), the java.util.concurrent.TimeoutException
will be thrown.
如果睡眠时间很长(超过 10 秒),java.util.concurrent.TimeoutException
则会抛出 。
I have seen this in the graphs generated from the analysis python script - for Android System Applications, not just my own monitored apps.
我在分析 python 脚本生成的图表中看到了这一点 - 对于 Android 系统应用程序,不仅仅是我自己的受监控应用程序。
Collect enough logs and you will eventually see it.
收集足够的日志,您最终会看到它。
Bottom line:
底线:
The issue cannot be avoided - you will encounter it if your app runs in the background.
这个问题无法避免 - 如果您的应用程序在后台运行,您就会遇到它。
You can mitigate by taking a WakeLock, and prevent the device from sleeping, but that is a different story altogether, and a new headache, and maybe another talk in another con.
你可以通过使用 WakeLock 来缓解,并防止设备进入睡眠状态,但那完全是另一回事,而且是一个新的头痛,也许是另一个骗局。
You can minimize the problem by reducing GC calls - making the scenario less likely (tips are in the slides).
您可以通过减少 GC 调用来最小化问题 - 使场景不太可能发生(提示在幻灯片中)。
I have not yet had the chance to go over the Dalvik 2 (a.k.a ART) GC code - which boasts a new Generational Compacting feature, or performed any experiments on an Android Lollipop.
我还没有机会仔细阅读 Dalvik 2(又名 ART)GC 代码——它拥有新的分代压缩功能,或者在 Android Lollipop 上进行任何实验。
Added 7/5/2015:
2015 年 7 月 5 日添加:
After reviewing the Crash reports aggregation for this crash type, it looks like these crashes from version 5.0+ of Android OS (Lollipop with ART) only account for 0.5% of this crash type. This means that the ART GC changes has reduced the frequency of these crashes.
查看此崩溃类型的崩溃报告汇总后,看起来来自 Android 操作系统 5.0+ 版本(Lollipop with ART)的这些崩溃仅占此崩溃类型的 0.5%。这意味着 ART GC 更改降低了这些崩溃的频率。
Added 6/1/2016:
2016 年 6 月 1 日添加:
Looks like the Android project has added a lot of info on how the GC works in Dalvik 2.0 (a.k.a ART).
看起来 Android 项目已经添加了很多关于 GC 如何在 Dalvik 2.0(又名 ART)中工作的信息。
You can read about it here - Debugging ART Garbage Collection.
您可以在此处阅读相关信息 -调试 ART 垃圾收集。
It also discusses some tools to get information on the GC behavior for your app.
它还讨论了一些工具来获取有关您的应用程序的 GC 行为的信息。
Sending a SIGQUIT to your app process will essentially cause an ANR, and dump the application state to a log file for analysis.
向您的应用进程发送 SIGQUIT 基本上会导致 ANR,并将应用程序状态转储到日志文件中以供分析。
回答by Kevin Coppock
We see this constantly, all over our app, using Crashlytics. The crash usually happens way down in platform code. A small sampling:
我们使用 Crashlytics 不断地在我们的应用程序中看到这一点。崩溃通常发生在平台代码中。一个小样本:
android.database.CursorWindow.finalize() timed out after 10 seconds
java.util.regex.Matcher.finalize() timed out after 10 seconds
android.graphics.Bitmap$BitmapFinalizer.finalize() timed out after 10 seconds
org.apache.http.impl.conn.SingleClientConnManager.finalize() timed out after 10 seconds
java.util.concurrent.ThreadPoolExecutor.finalize() timed out after 10 seconds
android.os.BinderProxy.finalize() timed out after 10 seconds
android.graphics.Path.finalize() timed out after 10 seconds
android.database.CursorWindow.finalize() 10 秒后超时
java.util.regex.Matcher.finalize() 10 秒后超时
android.graphics.Bitmap$BitmapFinalizer.finalize() 10 秒后超时
org.apache.http.impl.conn.SingleClientConnManager.finalize() 在 10 秒后超时
java.util.concurrent.ThreadPoolExecutor.finalize() 在 10 秒后超时
android.os.BinderProxy.finalize() 10 秒后超时
android.graphics.Path.finalize() 在 10 秒后超时
The devices on which this happens are overwhelmingly (but not exclusively) devices manufactured by Samsung. That could just mean that most of our users are using Samsung devices; alternately it could indicate a problem with Samsung devices. I'm not really sure.
发生这种情况的设备绝大多数(但并非唯一)是三星制造的设备。这可能只是意味着我们的大多数用户都在使用三星设备;或者,它可能表明三星设备存在问题。我不太确定。
I suppose this doesn't really answer your questions, but I just wanted to reinforce that this seems quite common, and is not specific to your application.
我想这并不能真正回答您的问题,但我只是想强调一下,这似乎很常见,并且并非特定于您的应用程序。
回答by Christopher
I found some slides about this issue.
我找到了一些关于这个问题的幻灯片。
In this slides the author tells that it seems to be a problem with GC, if there are a lot of objects or huge objects in heap. The slide also include a reference to a sample app and a python script to analyze this issue.
在这张幻灯片中,作者告诉我,如果堆中有很多对象或巨大的对象,这似乎是 GC 的问题。该幻灯片还包括对示例应用程序和用于分析此问题的 Python 脚本的引用。
https://github.com/oba2cat3/GCTest
https://github.com/oba2cat3/GCTest
https://github.com/oba2cat3/logcat2memorygraph
https://github.com/oba2cat3/logcat2memorygraph
Furthermore I found a hint in comment #3 on this side: https://code.google.com/p/android/issues/detail?id=53418#c3
此外,我在这一边的评论 #3 中发现了一个提示:https: //code.google.com/p/android/issues/detail?id=53418# c3
回答by Enaoi
We solved the problem by stopping the FinalizerWatchdogDaemon
.
我们通过停止FinalizerWatchdogDaemon
.
public static void fix() {
try {
Class clazz = Class.forName("java.lang.Daemons$FinalizerWatchdogDaemon");
Method method = clazz.getSuperclass().getDeclaredMethod("stop");
method.setAccessible(true);
Field field = clazz.getDeclaredField("INSTANCE");
field.setAccessible(true);
method.invoke(field.get(null));
}
catch (Throwable e) {
e.printStackTrace();
}
}
You can call the method in Application's lifecycle, like attachBaseContext()
.
For the same reason, you also can specific the phone's manufacture to fix the problem, it's up to you.
您可以在应用程序的生命周期中调用该方法,例如attachBaseContext()
. 出于同样的原因,您也可以指定手机的制造商来解决问题,这取决于您。
回答by danny117
Broadcast Receivers timeout after 10 seconds. Possibly your doing an asynchronous call (wrong) from a broadcast receiver and 4.3 actually detects it.
广播接收器在 10 秒后超时。可能您从广播接收器执行异步调用(错误),而 4.3 实际上检测到它。
回答by Sankalp Sharma
One thing which is invariably true is that at this time, the device would be suffocating for some memory (which is usually the reason for GC to most likely get triggered).
一件事总是正确的,在这个时候,设备会因为某些内存而窒息(这通常是 GC 最有可能被触发的原因)。
As mentioned by almost all authors earlier, this issue surfaces when Android tries to run GC while the app is in background. In most of the cases where we observed it, user paused the app by locking their screen. This might also indicate memory leak somewhere in the application, or the device being too loaded already. So the only legitimate way to minimize it is:
正如之前几乎所有作者所提到的,当 Android 尝试在应用程序处于后台时运行 GC 时,这个问题就会浮出水面。在我们观察到的大多数情况下,用户通过锁定屏幕来暂停应用程序。这也可能表明应用程序中某处存在内存泄漏,或者设备已经加载过多。因此,将其最小化的唯一合法方法是:
- to ensure there are no memory leaks, and
- to reduce the memory footprint of the app in general.
- 以确保没有内存泄漏,以及
- 总体上减少应用程序的内存占用。
回答by kiwi
Here is an effective solution from didi to solve this problem, Since this bug is very common and difficult to find the cause, It looks more like a system problem, Why can't we ignore it directly?Of course we can ignore it, Here is the sample code:
下面是滴滴解决这个问题的有效方案,由于这个bug很常见,很难找到原因,看起来更像是系统问题,为什么不能直接忽略呢?当然可以忽略,这里是示例代码:
final Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler =
Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
if (t.getName().equals("FinalizerWatchdogDaemon") && e instanceof TimeoutException) {
} else {
defaultUncaughtExceptionHandler.uncaughtException(t, e);
}
}
});
By setting a special default uncaught exception handler, application can change the way in which uncaught exceptions are handled for those threads that would already accept whatever default behavior the system provided. When an uncaught TimeoutException
is thrown from a thread named FinalizerWatchdogDaemon
, this special handler will block the handler chain, the system handler will not be called, so crash will be avoided.
通过设置一个特殊的默认未捕获异常处理程序,应用程序可以改变那些已经接受系统提供的任何默认行为的线程处理未捕获异常的方式。当一个TimeoutException
名为 的线程抛出一个未捕获的事件时FinalizerWatchdogDaemon
,这个特殊的处理程序将阻塞处理程序链,系统处理程序不会被调用,因此将避免崩溃。
Through practice, no other bad effects were found. The GC system is still working, timeouts are alleviated as CPU usage decreases.
通过实践,没有发现其他不良影响。GC 系统仍在工作,随着 CPU 使用率的降低,超时得到缓解。
For more details see: https://mp.weixin.qq.com/s/uFcFYO2GtWWiblotem2bGg
回答by kot32
try {
Class<?> c = Class.forName("java.lang.Daemons");
Field maxField = c.getDeclaredField("MAX_FINALIZE_NANOS");
maxField.setAccessible(true);
maxField.set(null, Long.MAX_VALUE);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
回答by vbevans94
It seems like a Android Runtime bug. There seems to be finalizer that runs in its separate thread and calls finalize() method on objects if they are not in the current frame of the stacktrace. For example following code(created to verify this issue) ended with the crash.
这似乎是一个 Android 运行时错误。似乎有终结器在其单独的线程中运行,如果对象不在堆栈跟踪的当前帧中,则对对象调用 finalize() 方法。例如,以下代码(创建以验证此问题)以崩溃结束。
Let's have some cursor that do something in finalize method(e.g. SqlCipher ones, do close() which locks to the database that is currently in use)
让我们有一些游标在 finalize 方法中做一些事情(例如 SqlCipher 的,做 close() 锁定到当前正在使用的数据库)
private static class MyCur extends MatrixCursor {
public MyCur(String[] columnNames) {
super(columnNames);
}
@Override
protected void finalize() {
super.finalize();
try {
for (int i = 0; i < 1000; i++)
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
And we do some long running stuff having opened cursor:
我们在打开游标后做了一些长时间运行的事情:
for (int i = 0; i < 7; i++) {
new Thread(new Runnable() {
@Override
public void run() {
MyCur cur = null;
try {
cur = new MyCur(new String[]{});
longRun();
} finally {
cur.close();
}
}
private void longRun() {
try {
for (int i = 0; i < 1000; i++)
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
This causes following error:
这会导致以下错误:
FATAL EXCEPTION: FinalizerWatchdogDaemon
Process: la.la.land, PID: 29206
java.util.concurrent.TimeoutException: MyCur.finalize() timed out after 10 seconds
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:371)
at java.lang.Thread.sleep(Thread.java:313)
at MyCur.finalize(MessageList.java:1791)
at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:222)
at java.lang.Daemons$FinalizerDaemon.run(Daemons.java:209)
at java.lang.Thread.run(Thread.java:762)
The production variant with SqlCipher is very similiar:
SqlCipher 的生产变体非常相似:
12-21 15:40:31.668: E/EH(32131): android.content.ContentResolver$CursorWrapperInner.finalize() timed out after 10 seconds
12-21 15:40:31.668: E/EH(32131): java.util.concurrent.TimeoutException: android.content.ContentResolver$CursorWrapperInner.finalize() timed out after 10 seconds
12-21 15:40:31.668: E/EH(32131): at java.lang.Object.wait(Native Method)
12-21 15:40:31.668: E/EH(32131): at java.lang.Thread.parkFor$(Thread.java:2128)
12-21 15:40:31.668: E/EH(32131): at sun.misc.Unsafe.park(Unsafe.java:325)
12-21 15:40:31.668: E/EH(32131): at java.util.concurrent.locks.LockSupport.park(LockSupport.java:161)
12-21 15:40:31.668: E/EH(32131): at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:840)
12-21 15:40:31.668: E/EH(32131): at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:873)
12-21 15:40:31.668: E/EH(32131): at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1197)
12-21 15:40:31.668: E/EH(32131): at java.util.concurrent.locks.ReentrantLock$FairSync.lock(ReentrantLock.java:200)
12-21 15:40:31.668: E/EH(32131): at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:262)
12-21 15:40:31.668: E/EH(32131): at net.sqlcipher.database.SQLiteDatabase.lock(SourceFile:518)
12-21 15:40:31.668: E/EH(32131): at net.sqlcipher.database.SQLiteProgram.close(SourceFile:294)
12-21 15:40:31.668: E/EH(32131): at net.sqlcipher.database.SQLiteQuery.close(SourceFile:136)
12-21 15:40:31.668: E/EH(32131): at net.sqlcipher.database.SQLiteCursor.close(SourceFile:510)
12-21 15:40:31.668: E/EH(32131): at android.database.CursorWrapper.close(CursorWrapper.java:50)
12-21 15:40:31.668: E/EH(32131): at android.database.CursorWrapper.close(CursorWrapper.java:50)
12-21 15:40:31.668: E/EH(32131): at android.content.ContentResolver$CursorWrapperInner.close(ContentResolver.java:2746)
12-21 15:40:31.668: E/EH(32131): at android.content.ContentResolver$CursorWrapperInner.finalize(ContentResolver.java:2757)
12-21 15:40:31.668: E/EH(32131): at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:222)
12-21 15:40:31.668: E/EH(32131): at java.lang.Daemons$FinalizerDaemon.run(Daemons.java:209)
12-21 15:40:31.668: E/EH(32131): at java.lang.Thread.run(Thread.java:762)
Resume: Close cursors ASAP.At least on Samsung S8 with Android 7 where the issue have been seen.
恢复:尽快关闭游标。至少在安装了 Android 7 的三星 S8 上已经发现该问题。
回答by Ben
For classes that you create (ie. are not part of the Android) its possible to avoid the crash completely.
对于您创建的类(即不是 Android 的一部分),可以完全避免崩溃。
Any class that implements finalize()
has some unavoidable probability of crashing as explained by @oba. So instead of using finalizers to perform cleanup, use a PhantomReferenceQueue
.
finalize()
正如@oba 所解释的那样,任何实现的类都有一些不可避免的崩溃概率。因此,不要使用终结器来执行清理,而是使用PhantomReferenceQueue
.
For an example check out the implementation in React Native: https://github.com/facebook/react-native/blob/master/ReactAndroid/src/main/java/com/facebook/jni/DestructorThread.java
例如,查看 React Native 中的实现:https: //github.com/facebook/react-native/blob/master/ReactAndroid/src/main/java/com/facebook/jni/DestructorThread.java