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 }