18
18
import com .google .common .annotations .VisibleForTesting ;
19
19
import com .google .common .base .MoreObjects ;
20
20
import com .google .common .base .Splitter ;
21
+ import com .google .devtools .build .lib .util .Pair ;
21
22
import com .google .devtools .common .options .OptionsParsingException ;
22
23
import java .io .OutputStream ;
23
24
import java .io .PrintStream ;
24
25
import java .lang .management .ManagementFactory ;
25
26
import java .lang .management .MemoryMXBean ;
26
27
import java .lang .management .MemoryUsage ;
27
28
import java .time .Duration ;
28
- import java .util .Iterator ;
29
+ import java .util .ArrayList ;
30
+ import java .util .List ;
29
31
import java .util .NoSuchElementException ;
30
32
import javax .annotation .Nullable ;
31
33
@@ -111,14 +113,29 @@ synchronized HeapAndNonHeap prepareBeanAndGetLocalMinUsage(
111
113
bean .gc ();
112
114
MemoryUsage minHeapUsed = bean .getHeapMemoryUsage ();
113
115
MemoryUsage minNonHeapUsed = bean .getNonHeapMemoryUsage ();
116
+
114
117
if (nextPhase == ProfilePhase .FINISH && memoryProfileStableHeapParameters != null ) {
115
- for (int i = 1 ; i < memoryProfileStableHeapParameters .numTimesToDoGc ; i ++) {
116
- sleeper .sleep (memoryProfileStableHeapParameters .timeToSleepBetweenGcs );
117
- bean .gc ();
118
- MemoryUsage currentHeapUsed = bean .getHeapMemoryUsage ();
119
- if (currentHeapUsed .getUsed () < minHeapUsed .getUsed ()) {
120
- minHeapUsed = currentHeapUsed ;
121
- minNonHeapUsed = bean .getNonHeapMemoryUsage ();
118
+ for (int j = 0 ; j < memoryProfileStableHeapParameters .gcSpecs .size (); j ++) {
119
+ Pair <Integer , Duration > spec = memoryProfileStableHeapParameters .gcSpecs .get (j );
120
+
121
+ int numTimesToDoGc = spec .first ;
122
+ Duration timeToSleepBetweenGcs = spec .second ;
123
+
124
+ for (int i = 0 ; i < numTimesToDoGc ; i ++) {
125
+ // We want to skip the first cycle for the first spec, since we ran a
126
+ // GC at the top of this function, but all the rest should get their
127
+ // proper runs
128
+ if (j == 0 && i == 0 ) {
129
+ continue ;
130
+ }
131
+
132
+ sleeper .sleep (timeToSleepBetweenGcs );
133
+ bean .gc ();
134
+ MemoryUsage currentHeapUsed = bean .getHeapMemoryUsage ();
135
+ if (currentHeapUsed .getUsed () < minHeapUsed .getUsed ()) {
136
+ minHeapUsed = currentHeapUsed ;
137
+ minNonHeapUsed = bean .getNonHeapMemoryUsage ();
138
+ }
122
139
}
123
140
}
124
141
}
@@ -130,12 +147,10 @@ synchronized HeapAndNonHeap prepareBeanAndGetLocalMinUsage(
130
147
* build.
131
148
*/
132
149
public static class MemoryProfileStableHeapParameters {
133
- private final int numTimesToDoGc ;
134
- private final Duration timeToSleepBetweenGcs ;
150
+ private final ArrayList <Pair <Integer , Duration >> gcSpecs ;
135
151
136
- private MemoryProfileStableHeapParameters (int numTimesToDoGc , Duration timeToSleepBetweenGcs ) {
137
- this .numTimesToDoGc = numTimesToDoGc ;
138
- this .timeToSleepBetweenGcs = timeToSleepBetweenGcs ;
152
+ private MemoryProfileStableHeapParameters (ArrayList <Pair <Integer , Duration >> gcSpecs ) {
153
+ this .gcSpecs = gcSpecs ;
139
154
}
140
155
141
156
/** Converter for {@code MemoryProfileStableHeapParameters} option. */
@@ -147,40 +162,48 @@ public static class Converter
147
162
@ Override
148
163
public MemoryProfileStableHeapParameters convert (String input )
149
164
throws OptionsParsingException {
150
- Iterator <String > values = SPLITTER .split (input ).iterator ();
165
+ List <String > values = SPLITTER .splitToList (input );
166
+
167
+ if (values .size () % 2 != 0 ) {
168
+ throw new OptionsParsingException (
169
+ "Expected even number of comma-separated integer values" );
170
+ }
171
+
172
+ ArrayList <Pair <Integer , Duration >> gcSpecs = new ArrayList <>(values .size () / 2 );
173
+
151
174
try {
152
- int numTimesToDoGc = Integer .parseInt (values .next ());
153
- int numSecondsToSleepBetweenGcs = Integer .parseInt (values .next ());
154
- if (values .hasNext ()) {
155
- throw new OptionsParsingException ("Expected exactly 2 comma-separated integer values" );
175
+ for (int i = 0 ; i < values .size (); i += 2 ) {
176
+ int numTimesToDoGc = Integer .parseInt (values .get (i ));
177
+ int numSecondsToSleepBetweenGcs = Integer .parseInt (values .get (i + 1 ));
178
+
179
+ if (numTimesToDoGc <= 0 ) {
180
+ throw new OptionsParsingException ("Number of times to GC must be positive" );
181
+ }
182
+ if (numSecondsToSleepBetweenGcs < 0 ) {
183
+ throw new OptionsParsingException (
184
+ "Number of seconds to sleep between GC's must be non-negative" );
185
+ }
186
+ gcSpecs .add (Pair .of (numTimesToDoGc , Duration .ofSeconds (numSecondsToSleepBetweenGcs )));
156
187
}
157
- if (numTimesToDoGc <= 0 ) {
158
- throw new OptionsParsingException ("Number of times to GC must be positive" );
159
- }
160
- if (numSecondsToSleepBetweenGcs < 0 ) {
161
- throw new OptionsParsingException (
162
- "Number of seconds to sleep between GC's must be non-negative" );
163
- }
164
- return new MemoryProfileStableHeapParameters (
165
- numTimesToDoGc , Duration .ofSeconds (numSecondsToSleepBetweenGcs ));
188
+
189
+ return new MemoryProfileStableHeapParameters (gcSpecs );
166
190
} catch (NumberFormatException | NoSuchElementException nfe ) {
167
191
throw new OptionsParsingException (
168
- "Expected exactly 2 comma-separated integer values" , nfe );
192
+ "Expected even number of comma-separated integer values, could not parse integer in"
193
+ + " list" ,
194
+ nfe );
169
195
}
170
196
}
171
197
172
198
@ Override
173
199
public String getTypeDescription () {
174
- return "two integers, separated by a comma" ;
200
+ return "integers, separated by a comma expected in pairs " ;
175
201
}
176
202
}
177
203
178
204
@ Override
179
205
public String toString () {
180
- return MoreObjects .toStringHelper (this )
181
- .add ("numTimesToDoGc" , numTimesToDoGc )
182
- .add ("timeToSleepBetweenGcs" , timeToSleepBetweenGcs )
183
- .toString ();
206
+ return MoreObjects .toStringHelper (this ).add ("gcSpecs" , gcSpecs ).toString ();
184
207
}
185
208
}
186
209
0 commit comments