xref: /aosp_15_r20/external/ow2-asm/benchmarks/src/jmh/java/org/objectweb/asm/benchmarks/MemoryProfiler.java (revision 2835e6bb194a25e32dae2cc0628d8f988b82bfc0)
1 // ASM: a very small and fast Java bytecode manipulation framework
2 // Copyright (c) 2000-2011 INRIA, France Telecom
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions
7 // are met:
8 // 1. Redistributions of source code must retain the above copyright
9 //    notice, this list of conditions and the following disclaimer.
10 // 2. Redistributions in binary form must reproduce the above copyright
11 //    notice, this list of conditions and the following disclaimer in the
12 //    documentation and/or other materials provided with the distribution.
13 // 3. Neither the name of the copyright holders nor the names of its
14 //    contributors may be used to endorse or promote products derived from
15 //    this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27 // THE POSSIBILITY OF SUCH DAMAGE.
28 package org.objectweb.asm.benchmarks;
29 
30 import java.lang.management.GarbageCollectorMXBean;
31 import java.lang.management.ManagementFactory;
32 import java.util.ArrayList;
33 import java.util.Collection;
34 import java.util.Collections;
35 import java.util.List;
36 import java.util.logging.Level;
37 import java.util.logging.Logger;
38 import javax.management.NotificationEmitter;
39 import org.openjdk.jmh.infra.BenchmarkParams;
40 import org.openjdk.jmh.infra.IterationParams;
41 import org.openjdk.jmh.profile.InternalProfiler;
42 import org.openjdk.jmh.results.AggregationPolicy;
43 import org.openjdk.jmh.results.IterationResult;
44 import org.openjdk.jmh.results.Result;
45 import org.openjdk.jmh.results.ScalarResult;
46 
47 /**
48  * An {@link InternalProfiler} to measure the memory allocated per benchmark iteration.
49  *
50  * @author Eric Bruneton
51  */
52 public class MemoryProfiler implements InternalProfiler {
53 
54   private static final Logger LOGGER = Logger.getLogger(MemoryProfiler.class.getName());
55 
56   private static Object[] references; // NOPMD(UnusedPrivateField): false positive.
57   private static int referenceCount;
58 
59   private static final MemoryProbe memoryProbe = new MemoryProbe();
60   private static long usedMemoryBeforeIteration;
61 
keepReference(final Object reference)62   public static void keepReference(final Object reference) {
63     references[referenceCount++] = reference;
64   }
65 
66   @Override
getDescription()67   public String getDescription() {
68     return "Adds used memory to the result.";
69   }
70 
71   @Override
beforeIteration( final BenchmarkParams benchmarkParams, final IterationParams iterationParams)72   public void beforeIteration(
73       final BenchmarkParams benchmarkParams, final IterationParams iterationParams) {
74     if (!appliesToBenchmark(benchmarkParams)) {
75       return;
76     }
77     references = new Object[100000];
78     referenceCount = 0;
79     usedMemoryBeforeIteration = memoryProbe.getUsedMemory();
80   }
81 
82   @Override
afterIteration( final BenchmarkParams benchmarkParams, final IterationParams iterationParams, final IterationResult result)83   public Collection<? extends Result> afterIteration(
84       final BenchmarkParams benchmarkParams,
85       final IterationParams iterationParams,
86       final IterationResult result) {
87     if (!appliesToBenchmark(benchmarkParams)) {
88       return Collections.emptyList();
89     }
90     long usedMemoryAfterIteration = memoryProbe.getUsedMemory();
91     references = null;
92 
93     long usedMemoryInIteration = usedMemoryAfterIteration - usedMemoryBeforeIteration;
94     double usedMemoryPerOp =
95         ((double) usedMemoryInIteration) / result.getMetadata().getMeasuredOps();
96     List<Result> results = new ArrayList<>();
97     results.add(new ScalarResult("+memory.used", usedMemoryPerOp, "bytes", AggregationPolicy.AVG));
98     return results;
99   }
100 
appliesToBenchmark(final BenchmarkParams benchmarkParams)101   private static boolean appliesToBenchmark(final BenchmarkParams benchmarkParams) {
102     return benchmarkParams.getBenchmark().contains("MemoryBenchmark");
103   }
104 
105   static class MemoryProbe {
106 
107     private static final int MAX_WAIT_MILLIS = 20 * 1000;
108 
109     private final Object lock = new Object();
110 
111     private int gcCount;
112 
MemoryProbe()113     public MemoryProbe() {
114       for (GarbageCollectorMXBean gcBean : ManagementFactory.getGarbageCollectorMXBeans()) {
115         ((NotificationEmitter) gcBean)
116             .addNotificationListener((notification, handback) -> notifyGc(), null, null);
117       }
118     }
119 
getUsedMemory()120     public long getUsedMemory() {
121       try {
122         systemGc(System.currentTimeMillis() + MAX_WAIT_MILLIS);
123       } catch (InterruptedException e) {
124         LOGGER.log(Level.WARNING, "Can't get used memory.");
125         return -1;
126       }
127       return ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed();
128     }
129 
notifyGc()130     private void notifyGc() {
131       synchronized (lock) {
132         gcCount++;
133         lock.notifyAll();
134       }
135     }
136 
systemGc(final long deadline)137     private void systemGc(final long deadline) throws InterruptedException {
138       synchronized (lock) {
139         System.gc(); // NOPMD(DoNotCallGarbageCollectionExplicitly): needed to measure used memory.
140         int previousGcCount = gcCount;
141         while (gcCount == previousGcCount) {
142           long timeout = deadline - System.currentTimeMillis();
143           if (timeout > 0) {
144             lock.wait(timeout);
145           } else {
146             throw new InterruptedException();
147           }
148         }
149       }
150     }
151   }
152 }
153