package com.carrotsearch.sizeof;
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.
*/
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryUsage;
import java.util.*;
import org.junit.*;
import org.junit.runner.JUnitCore;
import com.carrotsearch.sizeof.experiments.WildClasses;
/**
* Try to measure size estimation quality by allocating objects with known sizes
* and summing up the expectation until OOM.
*/
public class TestEstimationQuality {
static final long max;
static final long used;
static final long free;
static {
final MemoryUsage heapMemoryUsage =
ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
max = heapMemoryUsage.getMax();
used = heapMemoryUsage.getUsed();
free = max - used;
}
@Before
public void before() {
// Don't run on too large heaps unless forced.
if (System.getProperty("force") == null && free > 250 * RamUsageEstimator.ONE_MB) {
Assume.assumeTrue(false);
}
}
public static interface Allocator {
long newObject(Object[] vault, int i);
}
public static class ClassAllocator implements Allocator {
final long size;
public ClassAllocator(final Class<?> c) {
size = RamUsageEstimator.shallowSizeOfInstance(c);
}
@Override
public long newObject(Object[] vault, int i) {
vault[i] = new Object();
return size;
}
}
/**
* Simple Objects only. No fields, no nothing.
*/
@Test @Ignore
public void testObject() {
estimate(new ClassAllocator(Object.class));
}
/**
* Wild class instances (and arrays, etc.).
*/
@Test
public void testWildClasses() {
estimate(new Allocator() {
List<Class<?>> classes = new ArrayList<Class<?>>();
HashMap<Class<?>,Long> sizes = new HashMap<Class<?>,Long>();
Random random = new Random();
{
for (Class<?> c : WildClasses.ALL) {
try {
long size = RamUsageEstimator.sizeOf(c.newInstance());
// Uncomment to measure small objects only.
// if (size > 80) { continue; }
classes.add(c);
sizes.put(c, size);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
@Override
public long newObject(Object[] vault, int i) {
Class<?> c = classes.get(random.nextInt(classes.size()));
try {
vault[i] = c.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
if (!sizes.containsKey(c)) {
throw new RuntimeException();
}
return sizes.get(c);
}
});
}
/**
* Estimate estimation quality of {@link Allocator}'s objects.
*/
public void estimate(Allocator allocator) {
final MemoryUsage heapMemoryUsage =
ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
System.out.println(RamUsageEstimator.JVM_INFO_STRING);
System.out.println(
"Max: " + RamUsageEstimator.humanReadableUnits(max) +
", Used: " + RamUsageEstimator.humanReadableUnits(used) +
", Committed: " + RamUsageEstimator.humanReadableUnits(heapMemoryUsage.getCommitted()));
// Allocate an Object[] buffer for storing refs. Take 50% of available memory for it.
Object [] vault = new Object [(int) ((free / 2) / RamUsageEstimator.NUM_BYTES_OBJECT_REF)];
long expectedAllocated = 0;
try {
for (int i = 0; i < vault.length; i++) {
long j = allocator.newObject(vault, i);
expectedAllocated += j;
}
throw new RuntimeException();
} catch (OutOfMemoryError e) {
// Ignore.
}
Arrays.fill(vault, null);
long expectedFree = free - RamUsageEstimator.sizeOfAll(vault, allocator);
double difference = 100.0d * (expectedAllocated - expectedFree) / (double) expectedFree;
System.out.println(String.format(Locale.ENGLISH,
"Expected free: %s, Allocated estimation: %s, Difference: %.2f%% (%s)",
RamUsageEstimator.humanReadableUnits(expectedFree),
RamUsageEstimator.humanReadableUnits(expectedAllocated),
difference,
RamUsageEstimator.humanReadableUnits(Math.abs(expectedAllocated - expectedFree))
));
}
public static void main(String[] args) {
System.setProperty("force", "true");
JUnitCore.runClasses(TestEstimationQuality.class);
}
}