29
29
import java .util .ArrayList ;
30
30
import java .util .regex .Matcher ;
31
31
import java .util .regex .Pattern ;
32
+ import java .util .concurrent .CyclicBarrier ;
32
33
33
34
/*
34
35
* @test
39
40
* @run driver TestAlwaysPreTouchStacks
40
41
*/
41
42
42
- public class TestAlwaysPreTouchStacks extends Thread {
43
+ public class TestAlwaysPreTouchStacks {
43
44
44
- static private final Thread createTestThread (int stackSize ) {
45
+ // We will create a bunch of large-stacked threads to make a significant imprint on combined thread stack size
46
+ final static int MB = 1024 *1024 ;
47
+ static int memoryCeilingMB = 128 ;
48
+ static int threadStackSizeMB = 8 ;
49
+ static int numThreads = memoryCeilingMB / threadStackSizeMB ;
50
+ static CyclicBarrier gate = new CyclicBarrier (numThreads + 1 );
51
+
52
+ static private final Thread createTestThread (int num ) {
45
53
Thread t = new Thread (null ,
46
- () -> System .out .println ("Alive: " + stackSize ),
47
- "Thread-" + stackSize , stackSize );
54
+ () -> {
55
+ System .out .println ("Alive: " + num );
56
+ try {
57
+ // report aliveness, then sleep until VM death
58
+ gate .await ();
59
+ for (;;) {
60
+ Thread .sleep (1000 );
61
+ }
62
+ } catch (Exception e ) {
63
+ e .printStackTrace ();
64
+ }
65
+ },
66
+ "TestThread-" + num , threadStackSizeMB * MB );
67
+ t .setDaemon (true );
48
68
return t ;
49
69
}
50
70
51
- public static void main (String [] args ) throws InterruptedException , IOException {
52
-
53
- int [] stackSizes = {
54
- 1024 * 256 , 1024 * 512 , 1024 * 1024 * 3
55
- };
71
+ public static void main (String [] args ) throws Exception {
56
72
57
73
if (args .length == 1 && args [0 ].equals ("test" )) {
58
74
59
75
ArrayList <Thread > threads = new ArrayList <>();
60
76
61
- for (int s : stackSizes ) {
62
- threads .add (createTestThread (s ));
77
+ // Add a bunch of large-stacked threads to make a significant imprint on combined thread stack size
78
+ for (int i = 0 ; i < numThreads ; i ++) {
79
+ threads .add (createTestThread (i ));
63
80
}
64
81
82
+ // Start test threads.
65
83
threads .forEach (Thread ::start );
66
- for (Thread t : threads ) {
67
- t .join ();
68
- }
84
+
85
+ gate .await ();
86
+
87
+ // Stop VM. VM will run PrintNMTStatistics before exiting, and the still-running daemon threads
88
+ // should show up with fully - or almost fully - committed thread stacks.
69
89
70
90
} else {
71
91
72
92
ProcessBuilder pb = ProcessTools .createJavaProcessBuilder (
73
93
"-XX:+UnlockDiagnosticVMOptions" ,
74
94
"-Xmx100M" ,
75
- "-XX:+AlwaysPreTouchStacks" , "-Xlog:os+thread=trace" ,
95
+ "-XX:+AlwaysPreTouchStacks" ,
76
96
"-XX:NativeMemoryTracking=summary" , "-XX:+PrintNMTStatistics" ,
77
97
"TestAlwaysPreTouchStacks" ,
78
98
"test" );
@@ -81,12 +101,10 @@ public static void main(String[] args) throws InterruptedException, IOException
81
101
82
102
output .shouldHaveExitValue (0 );
83
103
84
- for (int s : stackSizes ) {
85
- output .shouldContain ("Alive: " + Integer . toString ( s ) );
104
+ for (int i = 0 ; i < numThreads ; i ++ ) {
105
+ output .shouldContain ("Alive: " + i );
86
106
}
87
107
88
- output .shouldContain ("Pretouching thread stack" );
89
-
90
108
// We want to see, in the final NMT printout, a committed thread stack size very close to reserved
91
109
// stack size. Like this:
92
110
// - Thread (reserved=10332400KB, committed=10284360KB)
@@ -108,9 +126,21 @@ public static void main(String[] args) throws InterruptedException, IOException
108
126
long reserved = Long .parseLong (m .group (1 ));
109
127
long committed = Long .parseLong (m .group (2 ));
110
128
System .out .println (">>>>> " + line + ": " + reserved + " - " + committed );
111
- if (committed < (reserved / 2 )) {
129
+ // This is a bit fuzzy: even with PreTouch we don't commit the full range of what NMT counts
130
+ // as thread stack. But without pre-touching, the thread stacks would be committed to about 1/5th
131
+ // of their reserved size. Requiring them to be committed for over 3/4th shows that pretouch is
132
+ // really working.
133
+ if ((double )committed < ((double )reserved * 0.75 )) {
112
134
throw new RuntimeException ("Expected a higher ratio between stack committed and reserved." );
113
135
}
136
+ // Added sanity tests: we expect our test threads to be still alive when NMT prints its final
137
+ // report, so their stacks should dominate the NMT-reported total stack size.
138
+ long max_reserved = memoryCeilingMB * 3 * MB ;
139
+ long min_reserved = memoryCeilingMB * MB ;
140
+ if (reserved >= max_reserved || reserved < min_reserved ) {
141
+ throw new RuntimeException ("Total reserved stack sizes outside of our expectations (" + reserved +
142
+ ", expected " + min_reserved + ".." + max_reserved + ")" );
143
+ }
114
144
foundLine = true ;
115
145
break ;
116
146
}
0 commit comments