JDK-7045138 : OutOfMemoryError thrown without stack trace in jdk7-b142
Type:Bug
Component:core-libs
Sub-Component:java.lang
Affected Version:7
Priority:P2
Status:Closed
Resolution:Fixed
OS:generic
CPU:generic
Submitted:2011-05-16
Updated:2012-02-02
Resolved:2011-06-10
The Version table provides details related to the release that this issue/RFE will be addressed.
Unresolved : Release in which this issue/RFE will be addressed. Resolved: Release in which this issue/RFE has been resolved. Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.
SUGGESTED FIX
With the patch below, all the java/lang/Throwable JDK regression tests pass; other test suites have been been run yet.
diff --git a/src/share/classes/java/lang/Throwable.java b/src/share/classes/java/lang/Throwable.java
--- a/src/share/classes/java/lang/Throwable.java
+++ b/src/share/classes/java/lang/Throwable.java
@@ -777,7 +777,8 @@
* @see java.lang.Throwable#printStackTrace()
*/
public synchronized Throwable fillInStackTrace() {
- if (stackTrace != null) {
+ if (stackTrace != null ||
+ backtrace != null /* Out of protocol state */ ) {
fillInStackTrace(0);
stackTrace = UNASSIGNED_STACK;
}
@@ -817,7 +818,8 @@
private synchronized StackTraceElement[] getOurStackTrace() {
// Initialize stack trace field with information from
// backtrace if this is the first call to this method
- if (stackTrace == UNASSIGNED_STACK) {
+ if (stackTrace == UNASSIGNED_STACK ||
+ (stackTrace == null && backtrace != null) /* Out of protocol state */) {
int depth = getStackTraceDepth();
stackTrace = new StackTraceElement[depth];
for (int i=0; i < depth; i++)
@@ -865,7 +867,8 @@
}
synchronized (this) {
- if (this.stackTrace == null) // Immutable stack
+ if (this.stackTrace == null && // Immutable stack
+ backtrace == null) // Test for out of protocol state
return;
this.stackTrace = defensiveCopy;
}
17-05-2011
EVALUATION
In essence a Throwable has an immutable stackTrace if (stackTrace==null && backtrace==null). So a complete fix has to address fillInStackTrace and setStackTrace as they would treat these VM instances as immutable when they are not. The patch that does this is:
765c765
< if (stackTrace != null || backtrace != null) {
---
> if (stackTrace != null) {
804,805c804
< if (stackTrace == UNASSIGNED_STACK ||
< (stackTrace == null && backtrace != null)) {
---
> if (stackTrace == UNASSIGNED_STACK) {
853c852
< if (this.stackTrace == null && backtrace == null) // Immutable stack
---
> if (this.stackTrace == null) // Immutable stack
This still muddies the "null implies immutability" story as it would need to expose the backtrace field which is purely an implementation detail.
An alternative fix would, of course, be to modify the VM to set the stackTrace field to UNASSIGNED_STACK after filling in the backtrace.
17-05-2011
EVALUATION
The VM creates a number of pre-allocated OOME instances using direct memory allocation that doesn't invoke an actual constructor. This leaves a number of null fields which are then treated as immutable in Throwable. However, some of the pre-allocated instances are intended to have stacktraces/backtraces. The VM will set the native backtrace on these instances, but the new logic in Throwable will see the null stackTrace and so ignore the fact a backtrace is available.
The current failure can be fixed as follows:
private synchronized StackTraceElement[] getOurStackTrace() {
// Initialize stack trace field with information from
// backtrace if this is the first call to this method
! if (stackTrace == UNASSIGNED_STACK ||
+ (stackTrace == null && backtrace != null) ) {
int depth = getStackTraceDepth();
stackTrace = new StackTraceElement[depth];
for (int i=0; i < depth; i++)
stackTrace[i] = getStackTraceElement(i);
} else if (stackTrace == null) {
return UNASSIGNED_STACK;
}
return stackTrace;
}
This fixes the current problem, but needs further testing. It also muddies the "null == immutable" story somewhat.