View Javadoc

    1   /*
    2    * Copyright 2009 Red Hat, Inc.
    3    *
    4    * Red Hat licenses this file to you under the Apache License, version 2.0
    5    * (the "License"); you may not use this file except in compliance with the
    6    * License.  You may obtain a copy of the License at:
    7    *
    8    *    http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
    13   * License for the specific language governing permissions and limitations
    14   * under the License.
    15   */
    16  package org.jboss.netty.buffer;
    17
    18  import java.lang.ref.ReferenceQueue;
    19  import java.nio.ByteBuffer;
    20  import java.nio.ByteOrder;
    21
    22  /**
    23   * A {@link ChannelBufferFactory} which pre-allocates a large chunk of direct
    24   * buffer and returns its slice on demand.  Direct buffers are reclaimed via
    25   * {@link ReferenceQueue} in most JDK implementations, and therefore they are
    26   * deallocated less efficiently than an ordinary heap buffer.  Consequently,
    27   * a user will get {@link OutOfMemoryError} when one tries to allocate small
    28   * direct buffers more often than the GC throughput of direct buffers, which
    29   * is much lower than the GC throughput of heap buffers.  This factory avoids
    30   * this problem by allocating a large chunk of pre-allocated direct buffer and
    31   * reducing the number of the garbage collected internal direct buffer objects.
    32   *
    33   * @author <a href="http://www.jboss.org/netty/">The Netty Project</a>
    34   * @author <a href="http://gleamynode.net/">Trustin Lee</a>
    35   * @version $Rev: 2293 $, $Date: 2010-06-01 17:38:51 +0900 (Tue, 01 Jun 2010) $
    36   */
    37  public class DirectChannelBufferFactory extends AbstractChannelBufferFactory {
    38
    39      private static final DirectChannelBufferFactory INSTANCE_BE =
    40          new DirectChannelBufferFactory(ByteOrder.BIG_ENDIAN);
    41
    42      private static final DirectChannelBufferFactory INSTANCE_LE =
    43          new DirectChannelBufferFactory(ByteOrder.LITTLE_ENDIAN);
    44
    45      public static ChannelBufferFactory getInstance() {
    46          return INSTANCE_BE;
    47      }
    48
    49      public static ChannelBufferFactory getInstance(ByteOrder defaultEndianness) {
    50          if (defaultEndianness == ByteOrder.BIG_ENDIAN) {
    51              return INSTANCE_BE;
    52          } else if (defaultEndianness == ByteOrder.LITTLE_ENDIAN) {
    53              return INSTANCE_LE;
    54          } else if (defaultEndianness == null) {
    55              throw new NullPointerException("defaultEndianness");
    56          } else {
    57              throw new IllegalStateException("Should not reach here");
    58          }
    59      }
    60
    61      private final Object bigEndianLock = new Object();
    62      private final Object littleEndianLock = new Object();
    63      private final int preallocatedBufferCapacity;
    64      private ChannelBuffer preallocatedBigEndianBuffer = null;
    65      private int preallocatedBigEndianBufferPosition;
    66      private ChannelBuffer preallocatedLittleEndianBuffer = null;
    67      private int preallocatedLittleEndianBufferPosition;
    68
    69      /**
    70       * Creates a new factory whose default {@link ByteOrder} is
    71       * {@link ByteOrder#BIG_ENDIAN}.
    72       */
    73      public DirectChannelBufferFactory() {
    74          this(ByteOrder.BIG_ENDIAN);
    75      }
    76
    77      /**
    78       * Creates a new factory whose default {@link ByteOrder} is
    79       * {@link ByteOrder#BIG_ENDIAN}.
    80       */
    81      public DirectChannelBufferFactory(int preallocatedBufferCapacity) {
    82          this(ByteOrder.BIG_ENDIAN, preallocatedBufferCapacity);
    83      }
    84
    85      /**
    86       * Creates a new factory with the specified default {@link ByteOrder}.
    87       *
    88       * @param defaultOrder the default {@link ByteOrder} of this factory
    89       */
    90      public DirectChannelBufferFactory(ByteOrder defaultOrder) {
    91          this(defaultOrder, 1048576);
    92      }
    93
    94      /**
    95       * Creates a new factory with the specified default {@link ByteOrder}.
    96       *
    97       * @param defaultOrder the default {@link ByteOrder} of this factory
    98       */
    99      public DirectChannelBufferFactory(ByteOrder defaultOrder, int preallocatedBufferCapacity) {
    100         super(defaultOrder);
    101         if (preallocatedBufferCapacity <= 0) {
    102             throw new IllegalArgumentException(
    103                     "preallocatedBufferCapacity must be greater than 0: " + preallocatedBufferCapacity);
    104         }
    105
    106         this.preallocatedBufferCapacity = preallocatedBufferCapacity;
    107     }
    108
    109     public ChannelBuffer getBuffer(ByteOrder order, int capacity) {
    110         if (order == null) {
    111             throw new NullPointerException("order");
    112         }
    113         if (capacity < 0) {
    114             throw new IllegalArgumentException("capacity: " + capacity);
    115         }
    116         if (capacity == 0) {
    117             return ChannelBuffers.EMPTY_BUFFER;
    118         }
    119         if (capacity >= preallocatedBufferCapacity) {
    120             return ChannelBuffers.directBuffer(order, capacity);
    121         }
    122
    123         ChannelBuffer slice;
    124         if (order == ByteOrder.BIG_ENDIAN) {
    125             slice = allocateBigEndianBuffer(capacity);
    126         } else {
    127             slice = allocateLittleEndianBuffer(capacity);
    128         }
    129         slice.clear();
    130         return slice;
    131     }
    132
    133     public ChannelBuffer getBuffer(ByteOrder order, byte[] array, int offset, int length) {
    134         if (array == null) {
    135             throw new NullPointerException("array");
    136         }
    137         if (offset < 0) {
    138             throw new IndexOutOfBoundsException("offset: " + offset);
    139         }
    140         if (length == 0) {
    141             return ChannelBuffers.EMPTY_BUFFER;
    142         }
    143         if (offset + length > array.length) {
    144             throw new IndexOutOfBoundsException("length: " + length);
    145         }
    146
    147         ChannelBuffer buf = getBuffer(order, length);
    148         buf.writeBytes(array, offset, length);
    149         return buf;
    150     }
    151
    152     public ChannelBuffer getBuffer(ByteBuffer nioBuffer) {
    153         if (!nioBuffer.isReadOnly() && nioBuffer.isDirect()) {
    154             return ChannelBuffers.wrappedBuffer(nioBuffer);
    155         }
    156
    157         ChannelBuffer buf = getBuffer(nioBuffer.order(), nioBuffer.remaining());
    158         int pos = nioBuffer.position();
    159         buf.writeBytes(nioBuffer);
    160         nioBuffer.position(pos);
    161         return buf;
    162     }
    163
    164     private ChannelBuffer allocateBigEndianBuffer(int capacity) {
    165         ChannelBuffer slice;
    166         synchronized (bigEndianLock) {
    167             if (preallocatedBigEndianBuffer == null) {
    168                 preallocatedBigEndianBuffer = ChannelBuffers.directBuffer(ByteOrder.BIG_ENDIAN, preallocatedBufferCapacity);
    169                 slice = preallocatedBigEndianBuffer.slice(0, capacity);
    170                 preallocatedBigEndianBufferPosition = capacity;
    171             } else if (preallocatedBigEndianBuffer.capacity() - preallocatedBigEndianBufferPosition >= capacity) {
    172                 slice = preallocatedBigEndianBuffer.slice(preallocatedBigEndianBufferPosition, capacity);
    173                 preallocatedBigEndianBufferPosition += capacity;
    174             } else {
    175                 preallocatedBigEndianBuffer = ChannelBuffers.directBuffer(ByteOrder.BIG_ENDIAN, preallocatedBufferCapacity);
    176                 slice = preallocatedBigEndianBuffer.slice(0, capacity);
    177                 preallocatedBigEndianBufferPosition = capacity;
    178             }
    179         }
    180         return slice;
    181     }
    182
    183     private ChannelBuffer allocateLittleEndianBuffer(int capacity) {
    184         ChannelBuffer slice;
    185         synchronized (littleEndianLock) {
    186             if (preallocatedLittleEndianBuffer == null) {
    187                 preallocatedLittleEndianBuffer = ChannelBuffers.directBuffer(ByteOrder.LITTLE_ENDIAN, preallocatedBufferCapacity);
    188                 slice = preallocatedLittleEndianBuffer.slice(0, capacity);
    189                 preallocatedLittleEndianBufferPosition = capacity;
    190             } else if (preallocatedLittleEndianBuffer.capacity() - preallocatedLittleEndianBufferPosition >= capacity) {
    191                 slice = preallocatedLittleEndianBuffer.slice(preallocatedLittleEndianBufferPosition, capacity);
    192                 preallocatedLittleEndianBufferPosition += capacity;
    193             } else {
    194                 preallocatedLittleEndianBuffer = ChannelBuffers.directBuffer(ByteOrder.LITTLE_ENDIAN, preallocatedBufferCapacity);
    195                 slice = preallocatedLittleEndianBuffer.slice(0, capacity);
    196                 preallocatedLittleEndianBufferPosition = capacity;
    197             }
    198         }
    199         return slice;
    200     }
    201 }