50113
|
1 |
/*
|
|
2 |
* Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
|
|
3 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
4 |
*
|
|
5 |
* This code is free software; you can redistribute it and/or modify it
|
|
6 |
* under the terms of the GNU General Public License version 2 only, as
|
|
7 |
* published by the Free Software Foundation. Oracle designates this
|
|
8 |
* particular file as subject to the "Classpath" exception as provided
|
|
9 |
* by Oracle in the LICENSE file that accompanied this code.
|
|
10 |
*
|
|
11 |
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
12 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
13 |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
14 |
* version 2 for more details (a copy is included in the LICENSE file that
|
|
15 |
* accompanied this code).
|
|
16 |
*
|
|
17 |
* You should have received a copy of the GNU General Public License version
|
|
18 |
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
19 |
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
20 |
*
|
|
21 |
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
22 |
* or visit www.oracle.com if you need additional information or have any
|
|
23 |
* questions.
|
|
24 |
*/
|
|
25 |
package jdk.test.lib.jfr;
|
|
26 |
|
|
27 |
import static jdk.test.lib.Asserts.assertEquals;
|
|
28 |
import static jdk.test.lib.Asserts.assertNotEquals;
|
|
29 |
import static jdk.test.lib.Asserts.assertNotNull;
|
|
30 |
import static jdk.test.lib.Asserts.assertNull;
|
|
31 |
import static jdk.test.lib.Asserts.fail;
|
|
32 |
|
|
33 |
import java.io.FileOutputStream;
|
|
34 |
import java.io.IOException;
|
|
35 |
import java.io.PrintStream;
|
|
36 |
import java.lang.management.GarbageCollectorMXBean;
|
|
37 |
import java.lang.management.ManagementFactory;
|
|
38 |
import java.time.Instant;
|
|
39 |
import java.util.ArrayList;
|
|
40 |
import java.util.Arrays;
|
|
41 |
import java.util.Collections;
|
|
42 |
import java.util.HashMap;
|
|
43 |
import java.util.HashSet;
|
|
44 |
import java.util.List;
|
|
45 |
import java.util.Map;
|
|
46 |
import java.util.Set;
|
|
47 |
import java.util.Stack;
|
|
48 |
|
|
49 |
import jdk.jfr.ValueDescriptor;
|
|
50 |
import jdk.jfr.consumer.RecordedEvent;
|
|
51 |
|
|
52 |
/**
|
|
53 |
* Mixed helper classes to test GC events.
|
|
54 |
*/
|
|
55 |
public class GCHelper {
|
|
56 |
public static final String event_garbage_collection = EventNames.GarbageCollection;
|
|
57 |
public static final String event_young_garbage_collection = EventNames.YoungGarbageCollection;
|
|
58 |
public static final String event_old_garbage_collection = EventNames.OldGarbageCollection;
|
|
59 |
public static final String event_parold_garbage_collection = EventNames.ParallelOldCollection;
|
|
60 |
public static final String event_g1_garbage_collection = EventNames.G1GarbageCollection;
|
|
61 |
public static final String event_heap_summary = EventNames.GCHeapSummary;
|
|
62 |
public static final String event_heap_ps_summary = EventNames.PSHeapSummary;
|
|
63 |
public static final String event_heap_metaspace_summary = EventNames.MetaspaceSummary;
|
|
64 |
public static final String event_reference_statistics = EventNames.GCReferenceStatistics;
|
|
65 |
public static final String event_phases_pause = EventNames.GCPhasePause;
|
|
66 |
public static final String event_phases_level_1 = EventNames.GCPhasePauseLevel1;
|
|
67 |
public static final String event_phases_level_2 = EventNames.GCPhasePauseLevel2;
|
|
68 |
public static final String event_phases_level_3 = EventNames.GCPhasePauseLevel3;
|
|
69 |
|
|
70 |
public static final String gcG1New = "G1New";
|
|
71 |
public static final String gcParNew = "ParNew";
|
|
72 |
public static final String gcDefNew = "DefNew";
|
|
73 |
public static final String gcParallelScavenge = "ParallelScavenge";
|
|
74 |
public static final String gcG1Old = "G1Old";
|
|
75 |
public static final String gcG1Full = "G1Full";
|
|
76 |
public static final String gcConcurrentMarkSweep = "ConcurrentMarkSweep";
|
|
77 |
public static final String gcSerialOld = "SerialOld";
|
|
78 |
public static final String gcPSMarkSweep = "PSMarkSweep";
|
|
79 |
public static final String gcParallelOld = "ParallelOld";
|
|
80 |
public static final String pauseLevelEvent = "GCPhasePauseLevel";
|
|
81 |
|
|
82 |
private static final List<String> g1HeapRegionTypes;
|
|
83 |
private static PrintStream defaultErrorLog = null;
|
|
84 |
|
|
85 |
public static int getGcId(RecordedEvent event) {
|
|
86 |
return Events.assertField(event, "gcId").getValue();
|
|
87 |
}
|
|
88 |
|
|
89 |
public static boolean isGcEvent(RecordedEvent event) {
|
|
90 |
for (ValueDescriptor v : event.getFields()) {
|
|
91 |
if ("gcId".equals(v.getName())) {
|
|
92 |
return true;
|
|
93 |
}
|
|
94 |
}
|
|
95 |
return false;
|
|
96 |
}
|
|
97 |
|
|
98 |
// public static String getEventDesc(RecordedEvent event) {
|
|
99 |
// final String path = event.getEventType().getName();
|
|
100 |
// if (!isGcEvent(event)) {
|
|
101 |
// return path;
|
|
102 |
// }
|
|
103 |
// if (event_garbage_collection.equals(path)) {
|
|
104 |
// String name = Events.assertField(event, "name").getValue();
|
|
105 |
// String cause = Events.assertField(event, "cause").getValue();
|
|
106 |
// return String.format("path=%s, gcId=%d, endTime=%d, name=%s, cause=%s, startTime=%d",
|
|
107 |
// path, getGcId(event), event.getEndTime(), name, cause, event.getStartTime());
|
|
108 |
// } else {
|
|
109 |
// return String.format("path=%s, gcId=%d, endTime=%d", path, getGcId(event), event.getEndTime());
|
|
110 |
// }
|
|
111 |
// }
|
|
112 |
|
|
113 |
public static RecordedEvent getConfigEvent(List<RecordedEvent> events) throws Exception {
|
|
114 |
for (RecordedEvent event : events) {
|
|
115 |
if (EventNames.GCConfiguration.equals(event.getEventType().getName())) {
|
|
116 |
return event;
|
|
117 |
}
|
|
118 |
}
|
|
119 |
fail("Could not find event " + EventNames.GCConfiguration);
|
|
120 |
return null;
|
|
121 |
}
|
|
122 |
|
|
123 |
public static void callSystemGc(int num, boolean withGarbage) {
|
|
124 |
for (int i = 0; i < num; i++) {
|
|
125 |
if (withGarbage) {
|
|
126 |
makeGarbage();
|
|
127 |
}
|
|
128 |
System.gc();
|
|
129 |
}
|
|
130 |
}
|
|
131 |
|
|
132 |
private static void makeGarbage() {
|
|
133 |
Object[] garbage = new Object[1024];
|
|
134 |
for (int i = 0; i < 1024; i++) {
|
|
135 |
garbage[i] = new Object();
|
|
136 |
}
|
|
137 |
}
|
|
138 |
|
|
139 |
// Removes gcEvents with lowest and highest gcID. This is used to filter out
|
|
140 |
// any incomplete GCs if the recording started/stopped in the middle of a GC.
|
|
141 |
// We also filters out events without gcId. Those events are not needed.
|
|
142 |
public static List<RecordedEvent> removeFirstAndLastGC(List<RecordedEvent> events) {
|
|
143 |
int minGcId = Integer.MAX_VALUE;
|
|
144 |
int maxGcId = Integer.MIN_VALUE;
|
|
145 |
// Find min/max gcId
|
|
146 |
for (RecordedEvent event : events) {
|
|
147 |
if (Events.hasField(event, "gcId")) {
|
|
148 |
int gcId = Events.assertField(event, "gcId").getValue();
|
|
149 |
minGcId = Math.min(gcId, minGcId);
|
|
150 |
maxGcId = Math.max(gcId, maxGcId);
|
|
151 |
}
|
|
152 |
}
|
|
153 |
|
|
154 |
// Add all events except those with gcId = min/max gcId
|
|
155 |
List<RecordedEvent> filteredEvents = new ArrayList<>();
|
|
156 |
for (RecordedEvent event : events) {
|
|
157 |
if (Events.hasField(event, "gcId")) {
|
|
158 |
int gcId = Events.assertField(event, "gcId").getValue();
|
|
159 |
if (gcId != minGcId && gcId != maxGcId) {
|
|
160 |
filteredEvents.add(event);
|
|
161 |
}
|
|
162 |
}
|
|
163 |
}
|
|
164 |
return filteredEvents;
|
|
165 |
}
|
|
166 |
|
|
167 |
public static Map<String, Boolean> beanCollectorTypes = new HashMap<>();
|
|
168 |
public static Set<String> collectorOverrides = new HashSet<>();
|
|
169 |
public static Map<String, String[]> requiredEvents = new HashMap<>();
|
|
170 |
|
|
171 |
static {
|
|
172 |
// young GarbageCollectionMXBeans.
|
|
173 |
beanCollectorTypes.put("G1 Young Generation", true);
|
|
174 |
beanCollectorTypes.put("Copy", true);
|
|
175 |
beanCollectorTypes.put("PS Scavenge", true);
|
|
176 |
beanCollectorTypes.put("ParNew", true);
|
|
177 |
|
|
178 |
// old GarbageCollectionMXBeans.
|
|
179 |
beanCollectorTypes.put("G1 Old Generation", false);
|
|
180 |
beanCollectorTypes.put("ConcurrentMarkSweep", false);
|
|
181 |
beanCollectorTypes.put("PS MarkSweep", false);
|
|
182 |
beanCollectorTypes.put("MarkSweepCompact", false);
|
|
183 |
|
|
184 |
// List of expected collector overrides. "A.B" means that collector A may use collector B.
|
|
185 |
collectorOverrides.add("G1Old.G1Full");
|
|
186 |
collectorOverrides.add("ConcurrentMarkSweep.SerialOld");
|
|
187 |
collectorOverrides.add("SerialOld.PSMarkSweep");
|
|
188 |
|
|
189 |
requiredEvents.put(gcG1New, new String[] {event_heap_summary, event_young_garbage_collection});
|
|
190 |
requiredEvents.put(gcParNew, new String[] {event_heap_summary, event_heap_metaspace_summary, event_phases_pause, event_phases_level_1, event_young_garbage_collection});
|
|
191 |
requiredEvents.put(gcDefNew, new String[] {event_heap_summary, event_heap_metaspace_summary, event_phases_pause, event_phases_level_1, event_young_garbage_collection});
|
|
192 |
requiredEvents.put(gcParallelScavenge, new String[] {event_heap_summary, event_heap_ps_summary, event_heap_metaspace_summary, event_reference_statistics, event_phases_pause, event_phases_level_1, event_young_garbage_collection});
|
|
193 |
requiredEvents.put(gcG1Old, new String[] {event_heap_summary, event_old_garbage_collection});
|
|
194 |
requiredEvents.put(gcG1Full, new String[] {event_heap_summary, event_heap_metaspace_summary, event_phases_pause, event_phases_level_1, event_old_garbage_collection});
|
|
195 |
requiredEvents.put(gcConcurrentMarkSweep, new String[] {event_phases_pause, event_phases_level_1, event_old_garbage_collection});
|
|
196 |
requiredEvents.put(gcSerialOld, new String[] {event_heap_summary, event_heap_metaspace_summary, event_phases_pause, event_phases_level_1, event_old_garbage_collection});
|
|
197 |
requiredEvents.put(gcParallelOld, new String[] {event_heap_summary, event_heap_ps_summary, event_heap_metaspace_summary, event_reference_statistics, event_phases_pause, event_phases_level_1, event_old_garbage_collection, event_parold_garbage_collection});
|
|
198 |
|
|
199 |
String[] g1HeapRegionTypeLiterals = new String[] {
|
|
200 |
"Free",
|
|
201 |
"Eden",
|
|
202 |
"Survivor",
|
|
203 |
"Starts Humongous",
|
|
204 |
"Continues Humongous",
|
|
205 |
"Old",
|
|
206 |
"Archive"
|
|
207 |
};
|
|
208 |
|
|
209 |
g1HeapRegionTypes = Collections.unmodifiableList(Arrays.asList(g1HeapRegionTypeLiterals));
|
|
210 |
}
|
|
211 |
|
|
212 |
/**
|
|
213 |
* Contains all GC events belonging to the same GC (same gcId).
|
|
214 |
*/
|
|
215 |
public static class GcBatch {
|
|
216 |
private List<RecordedEvent> events = new ArrayList<>();
|
|
217 |
|
|
218 |
public int getGcId() {
|
|
219 |
if (events.isEmpty()) {
|
|
220 |
return -1;
|
|
221 |
}
|
|
222 |
return GCHelper.getGcId(events.get(0));
|
|
223 |
}
|
|
224 |
|
|
225 |
public String getName() {
|
|
226 |
RecordedEvent endEvent = getEndEvent();
|
|
227 |
String name = endEvent == null ? null : Events.assertField(endEvent, "name").getValue();
|
|
228 |
return name == null ? "null" : name;
|
|
229 |
}
|
|
230 |
|
|
231 |
public RecordedEvent getEndEvent() {
|
|
232 |
return getEvent(event_garbage_collection);
|
|
233 |
}
|
|
234 |
|
|
235 |
public boolean addEvent(RecordedEvent event) {
|
|
236 |
if (!events.isEmpty()) {
|
|
237 |
assertEquals(getGcId(), GCHelper.getGcId(event), "Wrong gcId in event. Error in test code.");
|
|
238 |
}
|
|
239 |
boolean isEndEvent = event_garbage_collection.equals(event.getEventType().getName());
|
|
240 |
if (isEndEvent) {
|
|
241 |
// Verify that we have not already got a garbage_collection event with this gcId.
|
|
242 |
assertNull(getEndEvent(), String.format("Multiple %s for gcId %d", event_garbage_collection, getGcId()));
|
|
243 |
}
|
|
244 |
events.add(event);
|
|
245 |
return isEndEvent;
|
|
246 |
}
|
|
247 |
|
|
248 |
public boolean isYoungCollection() {
|
|
249 |
boolean isYoung = containsEvent(event_young_garbage_collection);
|
|
250 |
boolean isOld = containsEvent(event_old_garbage_collection);
|
|
251 |
assertNotEquals(isYoung, isOld, "isYoung and isOld was same for batch: " + toString());
|
|
252 |
return isYoung;
|
|
253 |
}
|
|
254 |
|
|
255 |
public int getEventCount() {
|
|
256 |
return events.size();
|
|
257 |
}
|
|
258 |
|
|
259 |
public RecordedEvent getEvent(int index) {
|
|
260 |
return events.get(index);
|
|
261 |
}
|
|
262 |
|
|
263 |
public List<RecordedEvent> getEvents() {
|
|
264 |
return events;
|
|
265 |
}
|
|
266 |
|
|
267 |
public RecordedEvent getEvent(String eventPath) {
|
|
268 |
for (RecordedEvent event : events) {
|
|
269 |
if (eventPath.equals(event.getEventType().getName())) {
|
|
270 |
return event;
|
|
271 |
}
|
|
272 |
}
|
|
273 |
return null;
|
|
274 |
}
|
|
275 |
|
|
276 |
public boolean containsEvent(String eventPath) {
|
|
277 |
return getEvent(eventPath) != null;
|
|
278 |
}
|
|
279 |
|
|
280 |
public String toString() {
|
|
281 |
RecordedEvent endEvent = getEndEvent();
|
|
282 |
Instant startTime = Instant.EPOCH;
|
|
283 |
String cause = "?";
|
|
284 |
String name = "?";
|
|
285 |
if (endEvent != null) {
|
|
286 |
name = getName();
|
|
287 |
startTime = endEvent.getStartTime();
|
|
288 |
cause = Events.assertField(endEvent, "cause").getValue();
|
|
289 |
}
|
|
290 |
return String.format("GcEvent: gcId=%d, method=%s, cause=%s, startTime=%s",
|
|
291 |
getGcId(), name, cause, startTime);
|
|
292 |
}
|
|
293 |
|
|
294 |
public String getLog() {
|
|
295 |
StringBuilder sb = new StringBuilder();
|
|
296 |
sb.append(this.toString() + System.getProperty("line.separator"));
|
|
297 |
for (RecordedEvent event : events) {
|
|
298 |
sb.append(String.format("event: %s%n", event));
|
|
299 |
}
|
|
300 |
return sb.toString();
|
|
301 |
}
|
|
302 |
|
|
303 |
// Group all events info batches.
|
|
304 |
public static List<GcBatch> createFromEvents(List<RecordedEvent> events) throws Exception {
|
|
305 |
Stack<Integer> openGcIds = new Stack<>();
|
|
306 |
List<GcBatch> batches = new ArrayList<>();
|
|
307 |
GcBatch currBatch = null;
|
|
308 |
|
|
309 |
for (RecordedEvent event : events) {
|
|
310 |
if (!isGcEvent(event)) {
|
|
311 |
continue;
|
|
312 |
}
|
|
313 |
int gcId = GCHelper.getGcId(event);
|
|
314 |
if (currBatch == null || currBatch.getGcId() != gcId) {
|
|
315 |
currBatch = null;
|
|
316 |
// Search for existing batch
|
|
317 |
for (GcBatch loopBatch : batches) {
|
|
318 |
if (gcId == loopBatch.getGcId()) {
|
|
319 |
currBatch = loopBatch;
|
|
320 |
break;
|
|
321 |
}
|
|
322 |
}
|
|
323 |
if (currBatch == null) {
|
|
324 |
// No existing batch. Create new.
|
|
325 |
currBatch = new GcBatch();
|
|
326 |
batches.add(currBatch);
|
|
327 |
openGcIds.push(new Integer(gcId));
|
|
328 |
}
|
|
329 |
}
|
|
330 |
boolean isEndEvent = currBatch.addEvent(event);
|
|
331 |
if (isEndEvent) {
|
|
332 |
openGcIds.pop();
|
|
333 |
}
|
|
334 |
}
|
|
335 |
// Verify that all start_garbage_collection events have received a corresponding "garbage_collection" event.
|
|
336 |
for (GcBatch batch : batches) {
|
|
337 |
if (batch.getEndEvent() == null) {
|
|
338 |
System.out.println(batch.getLog());
|
|
339 |
}
|
|
340 |
assertNotNull(batch.getEndEvent(), "GcBatch has no end event");
|
|
341 |
}
|
|
342 |
return batches;
|
|
343 |
}
|
|
344 |
}
|
|
345 |
|
|
346 |
/**
|
|
347 |
* Contains number of collections and sum pause time for young and old collections.
|
|
348 |
*/
|
|
349 |
public static class CollectionSummary {
|
|
350 |
public long collectionCountOld;
|
|
351 |
public long collectionCountYoung;
|
|
352 |
public long collectionTimeOld;
|
|
353 |
public long collectionTimeYoung;
|
|
354 |
private Set<String> names = new HashSet<>();
|
|
355 |
|
|
356 |
public void add(String collectorName, boolean isYoung, long count, long time) {
|
|
357 |
if (isYoung) {
|
|
358 |
collectionCountYoung += count;
|
|
359 |
collectionTimeYoung += time;
|
|
360 |
} else {
|
|
361 |
collectionCountOld += count;
|
|
362 |
collectionTimeOld += time;
|
|
363 |
}
|
|
364 |
if (!names.contains(collectorName)) {
|
|
365 |
names.add(collectorName);
|
|
366 |
}
|
|
367 |
}
|
|
368 |
|
|
369 |
public long sum() {
|
|
370 |
return collectionCountOld + collectionCountYoung;
|
|
371 |
}
|
|
372 |
|
|
373 |
public CollectionSummary calcDelta(CollectionSummary prev) {
|
|
374 |
CollectionSummary delta = new CollectionSummary();
|
|
375 |
delta.collectionCountOld = this.collectionCountOld - prev.collectionCountOld;
|
|
376 |
delta.collectionTimeOld = this.collectionTimeOld - prev.collectionTimeOld;
|
|
377 |
delta.collectionCountYoung = this.collectionCountYoung - prev.collectionCountYoung;
|
|
378 |
delta.collectionTimeYoung = this.collectionTimeYoung - prev.collectionTimeYoung;
|
|
379 |
delta.names.addAll(this.names);
|
|
380 |
delta.names.addAll(prev.names);
|
|
381 |
return delta;
|
|
382 |
}
|
|
383 |
|
|
384 |
public static CollectionSummary createFromMxBeans() {
|
|
385 |
CollectionSummary summary = new CollectionSummary();
|
|
386 |
List<GarbageCollectorMXBean> gcBeans = ManagementFactory.getGarbageCollectorMXBeans();
|
|
387 |
for (int c=0; c<gcBeans.size(); c++) {
|
|
388 |
GarbageCollectorMXBean currBean = gcBeans.get(c);
|
|
389 |
Boolean isYoung = beanCollectorTypes.get(currBean.getName());
|
|
390 |
assertNotNull(isYoung, "Unknown MXBean name: " + currBean.getName());
|
|
391 |
long collectionTime = currBean.getCollectionTime() * 1000; // Convert from millis to micros.
|
|
392 |
summary.add(currBean.getName(), isYoung.booleanValue(), currBean.getCollectionCount(), collectionTime);
|
|
393 |
}
|
|
394 |
return summary;
|
|
395 |
}
|
|
396 |
|
|
397 |
public static CollectionSummary createFromEvents(List<GcBatch> batches) {
|
|
398 |
CollectionSummary summary = new CollectionSummary();
|
|
399 |
for (GcBatch batch : batches) {
|
|
400 |
RecordedEvent endEvent = batch.getEndEvent();
|
|
401 |
assertNotNull(endEvent, "No end event in batch with gcId " + batch.getGcId());
|
|
402 |
String name = batch.getName();
|
|
403 |
summary.add(name, batch.isYoungCollection(), 1, Events.assertField(endEvent, "sumOfPauses").getValue());
|
|
404 |
}
|
|
405 |
return summary;
|
|
406 |
}
|
|
407 |
|
|
408 |
public String toString() {
|
|
409 |
StringBuilder collectorNames = new StringBuilder();
|
|
410 |
for (String s : names) {
|
|
411 |
if (collectorNames.length() > 0) {
|
|
412 |
collectorNames.append(", ");
|
|
413 |
}
|
|
414 |
collectorNames.append(s);
|
|
415 |
}
|
|
416 |
return String.format("CollectionSummary: young.collections=%d, young.time=%d, old.collections=%d, old.time=%d, collectors=(%s)",
|
|
417 |
collectionCountYoung, collectionTimeYoung, collectionCountOld, collectionTimeOld, collectorNames);
|
|
418 |
}
|
|
419 |
}
|
|
420 |
|
|
421 |
public static PrintStream getDefaultErrorLog() {
|
|
422 |
if (defaultErrorLog == null) {
|
|
423 |
try {
|
|
424 |
defaultErrorLog = new PrintStream(new FileOutputStream("error.log", true));
|
|
425 |
} catch (IOException e) {
|
|
426 |
e.printStackTrace();
|
|
427 |
defaultErrorLog = System.err;
|
|
428 |
}
|
|
429 |
}
|
|
430 |
return defaultErrorLog;
|
|
431 |
}
|
|
432 |
|
|
433 |
public static void log(Object msg) {
|
|
434 |
log(msg, System.err);
|
|
435 |
log(msg, getDefaultErrorLog());
|
|
436 |
}
|
|
437 |
|
|
438 |
public static void log(Object msg, PrintStream ps) {
|
|
439 |
ps.println(msg);
|
|
440 |
}
|
|
441 |
|
|
442 |
public static boolean isValidG1HeapRegionType(final String type) {
|
|
443 |
return g1HeapRegionTypes.contains(type);
|
|
444 |
}
|
|
445 |
|
|
446 |
/**
|
|
447 |
* Helper function to align heap size up.
|
|
448 |
*
|
|
449 |
* @param value
|
|
450 |
* @param alignment
|
|
451 |
* @return aligned value
|
|
452 |
*/
|
|
453 |
public static long alignUp(long value, long alignment) {
|
|
454 |
return (value + alignment - 1) & ~(alignment - 1);
|
|
455 |
}
|
|
456 |
|
|
457 |
/**
|
|
458 |
* Helper function to align heap size down.
|
|
459 |
*
|
|
460 |
* @param value
|
|
461 |
* @param alignment
|
|
462 |
* @return aligned value
|
|
463 |
*/
|
|
464 |
public static long alignDown(long value, long alignment) {
|
|
465 |
return value & ~(alignment - 1);
|
|
466 |
}
|
|
467 |
}
|