/** * Licensed to Cloudera, Inc. under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. Cloudera, Inc. licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.cloudera.flume.agent; import java.util.ArrayList; import org.junit.Assert; import org.junit.Test; import com.cloudera.flume.agent.MemoryMonitor.Listener; /** * This tests two cases -- the first where memory is exhausted but in a GC'able * state. The trigger function gc's and then continues * * The second is a case where gc will not free up enough memory. We mark failure * here (in the real use case, we would exit hard). * */ public class TestMemoryMonitor { static double threshold = .50; volatile boolean failed = false; /** * This forces a GC when memory is near a threshold. If after the gc we are * still above the threshold mark with the failed condition. */ @Test public void testNormalExhaust() { MemoryMonitor.setPercentageUsageThreshold(threshold); final MemoryMonitor mem = MemoryMonitor.getMemoryMonitor(); failed = false; Listener l = new Listener() { @Override public void memoryUsageLow(long usedMemory, long maxMemory) { System.out.printf("pre gc: memory usage %2.2f%%, %d used, %d max\n", ((double) usedMemory * 100) / (double) maxMemory, usedMemory / 1024 / 1024, maxMemory / 1024 / 1024); System.gc(); long umem = mem.getMemUsage(); long mmem = mem.getMemMax(); double percent = (umem) / mmem; System.out.printf("post gc: memory usage %2.2f%%, %d used, %d max\n", percent * 100, umem / 1024 / 1024, mmem / 1024 / 1024); if (percent > threshold) failed = true; // this happens in some random thread. } }; mem.addListener(l); // exhaust for (int i = 1; i < 1000; i++) { // allocated 10 megs at a time until it blows up. byte[] b = new byte[10 * 1024 * 1024]; b[0] = 0; if (failed) { Assert.fail("Out of memory!"); } } mem.removeListener(l); } @Test public void testExhaustRecover() { MemoryMonitor.setPercentageUsageThreshold(threshold); final MemoryMonitor mem = MemoryMonitor.getMemoryMonitor(); failed = false; Listener l = new Listener() { @Override public void memoryUsageLow(long usedMemory, long maxMemory) { System.out.printf("pre gc: memory usage %2.2f%%, %d used, %d max\n", ((double) usedMemory * 100) / (double) maxMemory, usedMemory / 1024 / 1024, maxMemory / 1024 / 1024); // System.gc(); Runtime.getRuntime().gc(); long umem = mem.getMemUsage(); long mmem = mem.getMemMax(); double percent = ((double) umem * 100) / (double) mmem; System.out.printf("post gc: memory usage %2.2f%%, %d used, %d max\n", percent, umem / 1024 / 1024, mmem / 1024 / 1024); if (percent > (threshold * 100)) failed = true; // this happens in some random thread. } }; mem.addListener(l); // exhaust try { ArrayList<byte[]> data = new ArrayList<byte[]>(); for (int i = 1; i < 1000; i++) { // allocated 10 megs at a time until it blows up -- keeping in a data // structure so it blows up. byte[] b = new byte[10 * 1024 * 1024]; b[0] = 0; data.add(b); System.out.printf("Megs allocated %d\n", i * 10); if (failed) { throw new RuntimeException("out of memory"); } } } catch (RuntimeException e) { // this is the expected case return; } catch (OutOfMemoryError oome) { Assert.fail("expected this to get caught and dealt with"); } finally { mem.removeListener(l); } Assert.fail("expected a failure"); } }