1 /*
  2  * Copyright (c) 2003, 2024, 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.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 package nsk.monitoring.stress.lowmem;
 24 
 25 import java.io.OutputStream;
 26 import java.io.PrintStream;
 27 import java.lang.management.ManagementFactory;
 28 import java.lang.management.MemoryMXBean;
 29 import java.lang.management.MemoryType;
 30 import java.util.LinkedList;
 31 import java.util.List;
 32 import java.util.concurrent.atomic.AtomicBoolean;
 33 import nsk.share.Log;
 34 import nsk.share.TestFailure;
 35 import nsk.share.gc.DefaultProducer;
 36 import nsk.share.gc.GC;
 37 import nsk.share.gc.ThreadedGCTest;
 38 import nsk.share.gc.gp.GarbageProducer;
 39 import nsk.share.gc.gp.classload.GeneratedClassProducer;
 40 import nsk.monitoring.share.*;
 41 import nsk.share.test.ExecutionController;
 42 
 43 public class lowmem001 extends ThreadedGCTest {
 44 
 45     // The max heap usage after whih we free memory and restart
 46     static final int MAX_HEAP_USAGE = 70;
 47     // isOOM is used to stop allocation and free resources
 48     // immediately after first OOME
 49     // really it could be only if we didn't check usage in time
 50     static AtomicBoolean isOOM = new AtomicBoolean(false);
 51     static ArgumentHandler argHandler;
 52     MemoryMonitor monitor;
 53 
 54     public static void main(String[] args) {
 55         argHandler = new ArgumentHandler(args);
 56         GC.runTest(new lowmem001(), args);
 57     }
 58 
 59     @Override
 60     public void run() {
 61         Log log = new Log(System.out);
 62         // System.err is duplicated into buffer
 63         // it should be empty
 64         MyStream stream = new MyStream(System.err);
 65         System.setErr(new PrintStream(stream));
 66 
 67         monitor = Monitor.getMemoryMonitor(log, argHandler);
 68         try {
 69             monitor.enableMonitoring();
 70             monitor.updateThresholds();
 71             super.run();
 72             monitor.disableMonitoring();
 73         } catch (Exception e) {
 74             throw new TestFailure(e);
 75         }
 76         if (isOOM.get() == true) {
 77             log.display("The OOME happened during test");
 78             // We control memory at 70 %
 79             // each time when we want to eat 512 bytes
 80             // if we got OOME it is really ugly
 81             throw new TestFailure("OOME should not happened.");
 82         }
 83         if (!monitor.getPassedStatus()) {
 84             throw new TestFailure("MemoryMonitor fails. See log.");
 85         }
 86         if (!stream.isEmpty()) {
 87             String string = stream.getString();
 88             if (string.contains("java.lang.OutOfMemoryError")) {
 89                 log.display("WARNING: The System.err contains OutOfMemory.");
 90                 // the OOME is not error
 91                 log.complain(string);
 92                 return;
 93             }
 94             log.complain(string);
 95             throw new TestFailure("Error stream is not empty.");
 96         }
 97 
 98     }
 99 
100     @Override
101     protected Runnable createRunnable(int i) {
102         String memory = argHandler.getTestedMemory();
103         if (memory.equals(MemoryMonitor.HEAP_TYPE)) {
104             return new HeapStresser();
105         }
106         if (memory.equals(MemoryMonitor.NONHEAP_TYPE)) {
107             return new ClassStresser();
108         }
109         // mixed type
110         return i % 2 == 0 ? new HeapStresser() : new ClassStresser();
111     }
112 
113     /*
114      * Simple ClassLoader is used for non-heap stressing
115      * should be revised after permgen removal
116      */
117     class ClassStresser extends Thread {
118 
119         @Override
120         public void run() {
121             ExecutionController stresser = getExecutionController();
122             GeneratedClassProducer gp = new GeneratedClassProducer();
123             while (stresser.continueExecution()) {
124                 try {
125                     gp.create(0);
126                 } catch (OutOfMemoryError e) {
127                     // drop 'gc', reset Thresholds and start new iteration
128                     monitor.resetThresholds(MemoryType.NON_HEAP);
129                     return;
130                 }
131             }
132         }
133     };
134 
135     class HeapStresser extends Thread {
136 
137         final long chunkSize = 512;
138         List storage;
139         GarbageProducer gp = new DefaultProducer();
140 
141 
142         @Override
143         public void run() {
144             storage = new LinkedList();
145             ExecutionController stresser = getExecutionController();
146             MemoryMXBean bean = ManagementFactory.getMemoryMXBean();
147 
148             while (stresser.continueExecution()) {
149                 try {
150                    storage.add(gp.create(chunkSize + new Object().hashCode() % 31));
151                    storage.add(gp.create(chunkSize));
152                    storage.remove(0);
153                     if (isOOM.get() == true || !stresser.continueExecution()) {
154                         stresser.finish();
155                         storage = null;
156                         return;
157                     }
158                     if (Thread.currentThread().isInterrupted()) {
159                         break;
160                     }
161                     // If memory is low free resources and restart
162                     if (bean.getHeapMemoryUsage().getUsed()
163                             > bean.getHeapMemoryUsage().getMax() * MAX_HEAP_USAGE / 100) {
164                         storage = new LinkedList();
165                         monitor.resetThresholds(MemoryType.HEAP);
166                     }
167                 } catch (OutOfMemoryError e) {
168                     // Let finish, the managment/memorymonitor could be
169                     // corrupted after OOME
170                     storage = null;
171                     isOOM.set(true);
172                     stresser.finish();
173                     return;
174                 }
175             }
176         }
177     }
178 
179     static class MyStream extends OutputStream {
180 
181         PrintStream err;
182 
183         public MyStream(PrintStream err) {
184             this.err = err;
185         }
186         private final static int SIZE = 100000;
187         private char[] value = new char[SIZE];
188         private int count = 0;
189 
190         // No additional memory allocation during write
191         @Override
192         public synchronized void write(int b) {
193             if (count < SIZE) {
194                 value[count++] = (char) b;
195             }
196             try {
197                 err.write(b);
198             } catch (OutOfMemoryError oome) {
199                 isOOM.set(true);
200             }
201         }
202 
203         public String getString() {
204             return new String(value, 0, count);
205         }
206 
207         public boolean isEmpty() {
208             return count == 0;
209         }
210     }
211 }