File: UnsafeFinalization.md

package info (click to toggle)
error-prone-java 2.18.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 23,204 kB
  • sloc: java: 222,992; xml: 1,319; sh: 25; makefile: 7
file content (71 lines) | stat: -rw-r--r-- 2,369 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
When invoking `static native` methods from objects that override
[`finalize`](https://docs.oracle.com/javase/9/docs/api/java/lang/Object.html#finalize--),
the finalizer may run before the native method finishes execution.

Consider the following example:

```java
public class GameRunner {
  // Pointer to some native resource, stored as a long
  private long nativeResourcePtr;

  // Allocates the native resource that this object "owns"
  private static native long doNativeInit();

  // Releases the native resource
  private static native void cleanUpNativeResources(long nativeResourcePtr);

  public GameRunner() {
    nativeResourcePtr = doNativeInit();
  }

  @Override
  protected void finalize() {
    cleanUpNativeResources(nativeResourcePtr);
    nativeResourcePtr = 0;
    super.finalize();
  }

  public void run() {
    GameLibrary.playGame(nativeResourcePtr); // Bug!
  }
}

public class GameLibrary {
  // Plays the game using the native resource
  public static native void playGame(long nativeResourcePtr);
}
```

During the execution of `GameRunner.run`, the call to `playGame` may not hold
the `this` reference live, and its finalizer may run, cleaning up the native
resources while the native code is still executing.

You can fix this by making the `static native` method not `static`, or by
changing the `static native` method so that it is passed the enclosing instance
and not the `nativeResourcePtr` value directly.

If you can use Java 9, the new method
[`Reference.reachabilityFence`](https://docs.oracle.com/javase/9/docs/api/java/lang/ref/Reference.html#reachabilityFence-java.lang.Object-)
will keep the reference passed in live. Be sure to call it in a finally block:

```java
 public void run() {
    try {
      playAwesomeGame(nativeResourcePtr);
    } finally {
      Reference.reachabilityFence(this);
    }
  }
}
```

Note: This check doesn't currently detect this problem when passing fields from
objects other than "this". That is equally unsafe. Consider passing the Java
object instead.

## References

*   [Boehm, "Destructors, finalizers, and synchronization." POPL 2003.](http://www.hpl.hp.com/techreports/2002/HPL-2002-335.html)
    Section 3.4 discusses this problem.
*   [Java Language Specification 12.6.2, "Interaction with the Memory Model."](https://docs.oracle.com/javase/specs/jls/se9/html/jls-12.html#jls-12.6.2)