All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.logging.log4j.util.Unbox Maven / Gradle / Ivy

The newest version!
/*
 * 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.logging.log4j.util;

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.status.StatusLogger;

/**
 * Utility for preventing primitive parameter values from being auto-boxed. Auto-boxing creates temporary objects
 * which contribute to pressure on the garbage collector. With this utility users can convert primitive values directly
 * into text without allocating temporary objects.
 * 

* Example usage: *

 * import static org.apache.logging.log4j.util.Unbox.box;
 * ...
 * long longValue = 123456L;
 * double doubleValue = 3.14;
 * // prevent primitive values from being auto-boxed
 * logger.debug("Long value={}, double value={}", box(longValue), box(doubleValue));
 * 
*

* This class manages a small thread-local ring buffer of StringBuilders. * Each time one of the {@code box()} methods is called, the next slot in the ring buffer is used, until the ring * buffer is full and the first slot is reused. By default the Unbox ring buffer has 32 slots, so user code can * have up to 32 boxed primitives in a single logger call. *

*

* If more slots are required, set system property {@code log4j.unbox.ringbuffer.size} to the desired ring buffer size. * Note that the specified number will be rounded up to the nearest power of 2. *

* @since 2.6 */ @PerformanceSensitive("allocation") public class Unbox { private static final Logger LOGGER = StatusLogger.getLogger(); private static final int BITS_PER_INT = 32; private static final int RINGBUFFER_MIN_SIZE = 32; private static final int RINGBUFFER_SIZE = calculateRingBufferSize("log4j.unbox.ringbuffer.size"); private static final int MASK = RINGBUFFER_SIZE - 1; /** * State implementation that only puts JDK classes in ThreadLocals, so this is safe to be used from * web applications. Web application containers have thread pools that may hold on to ThreadLocal objects * after the application was stopped. This may prevent the classes of the application from being unloaded, * causing memory leaks. *

* Such memory leaks will not occur if only JDK classes are stored in ThreadLocals. *

*/ private static class WebSafeState { private final ThreadLocal ringBuffer = new ThreadLocal<>(); private final ThreadLocal current = new ThreadLocal<>(); public StringBuilder getStringBuilder() { StringBuilder[] array = ringBuffer.get(); if (array == null) { array = new StringBuilder[RINGBUFFER_SIZE]; for (int i = 0; i < array.length; i++) { array[i] = new StringBuilder(21); } ringBuffer.set(array); current.set(new int[1]); } final int[] index = current.get(); final StringBuilder result = array[MASK & index[0]++]; result.setLength(0); return result; } public boolean isBoxedPrimitive(final StringBuilder text) { final StringBuilder[] array = ringBuffer.get(); if (array == null) { return false; } for (int i = 0; i < array.length; i++) { if (text == array[i]) { return true; } } return false; } } private static class State { private final StringBuilder[] ringBuffer = new StringBuilder[RINGBUFFER_SIZE]; private int current; State() { for (int i = 0; i < ringBuffer.length; i++) { ringBuffer[i] = new StringBuilder(21); } } public StringBuilder getStringBuilder() { final StringBuilder result = ringBuffer[MASK & current++]; result.setLength(0); return result; } public boolean isBoxedPrimitive(final StringBuilder text) { for (int i = 0; i < ringBuffer.length; i++) { if (text == ringBuffer[i]) { return true; } } return false; } } private static ThreadLocal threadLocalState = new ThreadLocal<>(); private static WebSafeState webSafeState = new WebSafeState(); private Unbox() { // this is a utility } private static int calculateRingBufferSize(final String propertyName) { final String userPreferredRBSize = PropertiesUtil.getProperties().getStringProperty(propertyName, String.valueOf(RINGBUFFER_MIN_SIZE)); try { int size = Integer.parseInt(userPreferredRBSize); if (size < RINGBUFFER_MIN_SIZE) { size = RINGBUFFER_MIN_SIZE; LOGGER.warn("Invalid {} {}, using minimum size {}.", propertyName, userPreferredRBSize, RINGBUFFER_MIN_SIZE); } return ceilingNextPowerOfTwo(size); } catch (final Exception ex) { LOGGER.warn("Invalid {} {}, using default size {}.", propertyName, userPreferredRBSize, RINGBUFFER_MIN_SIZE); return RINGBUFFER_MIN_SIZE; } } /** * Calculate the next power of 2, greater than or equal to x. *

* From Hacker's Delight, Chapter 3, Harry S. Warren Jr. * * @param x Value to round up * @return The next power of 2 from x inclusive */ private static int ceilingNextPowerOfTwo(final int x) { return 1 << (BITS_PER_INT - Integer.numberOfLeadingZeros(x - 1)); } /** * Returns a {@code StringBuilder} containing the text representation of the specified primitive value. * This method will not allocate temporary objects. * * @param value the value whose text representation to return * @return a {@code StringBuilder} containing the text representation of the specified primitive value */ @PerformanceSensitive("allocation") public static StringBuilder box(final float value) { return getSB().append(value); } /** * Returns a {@code StringBuilder} containing the text representation of the specified primitive value. * This method will not allocate temporary objects. * * @param value the value whose text representation to return * @return a {@code StringBuilder} containing the text representation of the specified primitive value */ @PerformanceSensitive("allocation") public static StringBuilder box(final double value) { return getSB().append(value); } /** * Returns a {@code StringBuilder} containing the text representation of the specified primitive value. * This method will not allocate temporary objects. * * @param value the value whose text representation to return * @return a {@code StringBuilder} containing the text representation of the specified primitive value */ @PerformanceSensitive("allocation") public static StringBuilder box(final short value) { return getSB().append(value); } /** * Returns a {@code StringBuilder} containing the text representation of the specified primitive value. * This method will not allocate temporary objects. * * @param value the value whose text representation to return * @return a {@code StringBuilder} containing the text representation of the specified primitive value */ @PerformanceSensitive("allocation") public static StringBuilder box(final int value) { return getSB().append(value); } /** * Returns a {@code StringBuilder} containing the text representation of the specified primitive value. * This method will not allocate temporary objects. * * @param value the value whose text representation to return * @return a {@code StringBuilder} containing the text representation of the specified primitive value */ @PerformanceSensitive("allocation") public static StringBuilder box(final char value) { return getSB().append(value); } /** * Returns a {@code StringBuilder} containing the text representation of the specified primitive value. * This method will not allocate temporary objects. * * @param value the value whose text representation to return * @return a {@code StringBuilder} containing the text representation of the specified primitive value */ @PerformanceSensitive("allocation") public static StringBuilder box(final long value) { return getSB().append(value); } /** * Returns a {@code StringBuilder} containing the text representation of the specified primitive value. * This method will not allocate temporary objects. * * @param value the value whose text representation to return * @return a {@code StringBuilder} containing the text representation of the specified primitive value */ @PerformanceSensitive("allocation") public static StringBuilder box(final byte value) { return getSB().append(value); } /** * Returns a {@code StringBuilder} containing the text representation of the specified primitive value. * This method will not allocate temporary objects. * * @param value the value whose text representation to return * @return a {@code StringBuilder} containing the text representation of the specified primitive value */ @PerformanceSensitive("allocation") public static StringBuilder box(final boolean value) { return getSB().append(value); } private static State getState() { State state = threadLocalState.get(); if (state == null) { state = new State(); threadLocalState.set(state); } return state; } private static StringBuilder getSB() { return Constants.ENABLE_THREADLOCALS ? getState().getStringBuilder() : webSafeState.getStringBuilder(); } /** For testing. */ static int getRingbufferSize() { return RINGBUFFER_SIZE; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy