blob: 52b5bbc55884d7b6fc111786d7575f61778f2532 [file] [log] [blame]
/*
* 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.
*/
package org.apache.uima.cas.impl;
/**
* Encapsulate 8, 16, and 64 bit storage for the CAS.
*/
abstract class CommonAuxHeap {
private static final boolean DEBUG_LOG_SHRINK = false;
// static {
// debugLogShrink = System.getProperty("uima.debug.ihs") != null;
// }
// cannot be 0 because it grows by multiplying growth_factor
protected static final int DEFAULT_HEAP_BASE_SIZE = 16;
// Jira: https://issues.apache.org/jira/browse/UIMA-2385,
// https://issues.apache.org/jira/browse/UIMA-4279
protected static final int DEFAULT_HEAP_MULT_LIMIT = 1024 * 1024 * 16;
protected static final int MIN_HEAP_BASE_SIZE = 16;
protected static final int GROWTH_FACTOR = 2;
protected static final int NULL = 0;
// start pos
protected static final int FIRST_CELL_REF = 1;
protected final int heapBaseSize;
protected final int heapMultLimit;
protected int heapPos = FIRST_CELL_REF;
private final int[] shrinkableCount = new int[1];
CommonAuxHeap() {
this(DEFAULT_HEAP_BASE_SIZE, DEFAULT_HEAP_MULT_LIMIT);
}
CommonAuxHeap(int heapBaseSize, int heapMultLimit) {
this.heapBaseSize = Math.max(heapBaseSize, MIN_HEAP_BASE_SIZE);
this.heapMultLimit = Math.max(heapMultLimit, DEFAULT_HEAP_MULT_LIMIT);
initMemory();
}
abstract void initMemory();
abstract void initMemory(int size);
abstract void resetToZeros();
abstract void growHeapIfNeeded();
void reset() {
this.reset(false);
}
/**
* Logic for shrinking:
*
* Based on a short history of the capacity needed to hold the larger of the previous 2 sizes
* (Note: can be overridden by calling reset() multiple times in a row) Never shrink below
* initialSize
*
* Shrink in exact reverse sequence of growth - using the subtraction method and then (for small
* enough sizes) the dividing method
*
* Shrink by one jump if that is large enough to hold the larger of the prev 2 sizes
*
* @param doFullReset
* true means reallocate from scratch
*/
void reset(boolean doFullReset) {
if (doFullReset) {
if (DEBUG_LOG_SHRINK)
System.out.format("Debug shrink CommonAux full reset from %,d to %,d for %s%n",
getCapacity(), heapBaseSize, this.getClass().getSimpleName());
this.initMemory();
} else {
final int curCapacity = getCapacity();
final int curSize = getSize();
int newSize = computeShrunkArraySize(curCapacity, curSize, GROWTH_FACTOR, heapMultLimit,
heapBaseSize, shrinkableCount);
if (newSize == getCapacity()) { // means didn't shrink
resetToZeros();
} else {
if (DEBUG_LOG_SHRINK)
System.out.format("Debug shrink CommonAux from %,d to %,d for %s%n", curCapacity, newSize,
this.getClass().getSimpleName());
initMemory(newSize);
}
}
heapPos = FIRST_CELL_REF;
}
int reserve(int numCells) {
int cellRef = heapPos;
heapPos += numCells;
growHeapIfNeeded();
return cellRef;
}
int computeNewArraySize(int size, int needed_size, int growth_factor, int multiplication_limit) {
do {
if (size < multiplication_limit) {
size *= growth_factor;
} else {
size += multiplication_limit;
}
} while (size < needed_size);
return size;
}
/**
* This routine is used to compute the capacity an internal expandable array should be reallocated
* to, upon reset.
*
* It returns at most a 1 increment shrink, based on the doubling up to multiplication limit, then
* addition thereafter.
*
* It maintains a shrinkableCount - the number of consecutive times this could be shrunk. This is
* reset to 0 if current size requires current capacity, that is, no shrinkage is possible for
* current size. Otherwise, the count is incremented.
*
* If the shrinkableCount is incremented to exceed 20, the capacity is allowed to drop by 1
* allocation unit.
*
* This guarantees the shrinkages are delayed until 20 shrinkable sizes are found (with no
* intervening non-shrinkable ones). When a shrinkage happens, the count is reset to 16; this
* delays subsequent shrinkages to happen only every (20 - 16) 4 resets.
*
* @param capacity
* the current capacity
* @param size_used
* the maximum number of used entries, <= current capacity
* @param growth_factor
* is 2
* @param multiplication_limit
* the point where we start adding this limit, vs using the growth factor
* @param shrinkableCount
* a pass-by-reference int reflecting the number of times it was shrinkable
* @return the capacity shrink down by one step, if that will still hold the size_used number of
* entries, minimum limited to min_size.
*/
static int computeShrunkArraySize(int capacity, int size_used, int growth_factor,
int multiplication_limit, int min_size, int[] shrinkableCount) { // pass by reference
if (capacity < size_used) {
throw new IllegalArgumentException(
"The Capacity " + capacity + " must be >= sized_used " + size_used);
}
// this if for shrinking down 1 step if possible
int oneSizeLowerCapacity = ((capacity - multiplication_limit) < multiplication_limit) ?
// the last expansion was by multiplying; the next expansion would be by adding
(capacity / growth_factor) : (capacity - multiplication_limit);
if (oneSizeLowerCapacity < min_size) {
return capacity;
}
boolean isShrink = (size_used < oneSizeLowerCapacity);
if (isShrink) {
shrinkableCount[0]++;
if (shrinkableCount[0] > 20) {
shrinkableCount[0] = 16;
return oneSizeLowerCapacity;
}
return capacity;
}
shrinkableCount[0] = 0;
return capacity;
}
int getSize() {
return heapPos;
}
abstract int getCapacity();
}