View Javadoc

    1   /*
    2    * Copyright 2012 The Netty Project
    3    *
    4    * The Netty Project licenses this file to you under the Apache License,
    5    * version 2.0 (the "License"); you may not use this file except in compliance
    6    * with the 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  public class DirectChannelBufferFactory extends AbstractChannelBufferFactory {
    34
    35      private static final DirectChannelBufferFactory INSTANCE_BE =
    36          new DirectChannelBufferFactory(ByteOrder.BIG_ENDIAN);
    37
    38      private static final DirectChannelBufferFactory INSTANCE_LE =
    39          new DirectChannelBufferFactory(ByteOrder.LITTLE_ENDIAN);
    40
    41      public static ChannelBufferFactory getInstance() {
    42          return INSTANCE_BE;
    43      }
    44
    45      public static ChannelBufferFactory getInstance(ByteOrder defaultEndianness) {
    46          if (defaultEndianness == ByteOrder.BIG_ENDIAN) {
    47              return INSTANCE_BE;
    48          } else if (defaultEndianness == ByteOrder.LITTLE_ENDIAN) {
    49              return INSTANCE_LE;
    50          } else if (defaultEndianness == null) {
    51              throw new NullPointerException("defaultEndianness");
    52          } else {
    53              throw new IllegalStateException("Should not reach here");
    54          }
    55      }
    56
    57      private final Object bigEndianLock = new Object();
    58      private final Object littleEndianLock = new Object();
    59      private final int preallocatedBufCapacity;
    60      private ChannelBuffer preallocatedBEBuf;
    61      private int preallocatedBEBufPos;
    62      private ChannelBuffer preallocatedLEBuf;
    63      private int preallocatedLEBufPos;
    64
    65      /**
    66       * Creates a new factory whose default {@link ByteOrder} is
    67       * {@link ByteOrder#BIG_ENDIAN}.
    68       */
    69      public DirectChannelBufferFactory() {
    70          this(ByteOrder.BIG_ENDIAN);
    71      }
    72
    73      /**
    74       * Creates a new factory whose default {@link ByteOrder} is
    75       * {@link ByteOrder#BIG_ENDIAN}.
    76       */
    77      public DirectChannelBufferFactory(int preallocatedBufferCapacity) {
    78          this(ByteOrder.BIG_ENDIAN, preallocatedBufferCapacity);
    79      }
    80
    81      /**
    82       * Creates a new factory with the specified default {@link ByteOrder}.
    83       *
    84       * @param defaultOrder the default {@link ByteOrder} of this factory
    85       */
    86      public DirectChannelBufferFactory(ByteOrder defaultOrder) {
    87          this(defaultOrder, 1048576);
    88      }
    89
    90      /**
    91       * Creates a new factory with the specified default {@link ByteOrder}.
    92       *
    93       * @param defaultOrder the default {@link ByteOrder} of this factory
    94       */
    95      public DirectChannelBufferFactory(ByteOrder defaultOrder, int preallocatedBufferCapacity) {
    96          super(defaultOrder);
    97          if (preallocatedBufferCapacity <= 0) {
    98              throw new IllegalArgumentException(
    99                      "preallocatedBufCapacity must be greater than 0: " + preallocatedBufferCapacity);
    100         }
    101
    102         this.preallocatedBufCapacity = preallocatedBufferCapacity;
    103     }
    104
    105     public ChannelBuffer getBuffer(ByteOrder order, int capacity) {
    106         if (order == null) {
    107             throw new NullPointerException("order");
    108         }
    109         if (capacity < 0) {
    110             throw new IllegalArgumentException("capacity: " + capacity);
    111         }
    112         if (capacity == 0) {
    113             return ChannelBuffers.EMPTY_BUFFER;
    114         }
    115         if (capacity >= preallocatedBufCapacity) {
    116             return ChannelBuffers.directBuffer(order, capacity);
    117         }
    118
    119         ChannelBuffer slice;
    120         if (order == ByteOrder.BIG_ENDIAN) {
    121             slice = allocateBigEndianBuffer(capacity);
    122         } else {
    123             slice = allocateLittleEndianBuffer(capacity);
    124         }
    125         slice.clear();
    126         return slice;
    127     }
    128
    129     public ChannelBuffer getBuffer(ByteOrder order, byte[] array, int offset, int length) {
    130         if (array == null) {
    131             throw new NullPointerException("array");
    132         }
    133         if (offset < 0) {
    134             throw new IndexOutOfBoundsException("offset: " + offset);
    135         }
    136         if (length == 0) {
    137             return ChannelBuffers.EMPTY_BUFFER;
    138         }
    139         if (offset + length > array.length) {
    140             throw new IndexOutOfBoundsException("length: " + length);
    141         }
    142
    143         ChannelBuffer buf = getBuffer(order, length);
    144         buf.writeBytes(array, offset, length);
    145         return buf;
    146     }
    147
    148     public ChannelBuffer getBuffer(ByteBuffer nioBuffer) {
    149         if (!nioBuffer.isReadOnly() && nioBuffer.isDirect()) {
    150             return ChannelBuffers.wrappedBuffer(nioBuffer);
    151         }
    152
    153         ChannelBuffer buf = getBuffer(nioBuffer.order(), nioBuffer.remaining());
    154         int pos = nioBuffer.position();
    155         buf.writeBytes(nioBuffer);
    156         nioBuffer.position(pos);
    157         return buf;
    158     }
    159
    160     private ChannelBuffer allocateBigEndianBuffer(int capacity) {
    161         ChannelBuffer slice;
    162         synchronized (bigEndianLock) {
    163             if (preallocatedBEBuf == null) {
    164                 preallocatedBEBuf = ChannelBuffers.directBuffer(ByteOrder.BIG_ENDIAN, preallocatedBufCapacity);
    165                 slice = preallocatedBEBuf.slice(0, capacity);
    166                 preallocatedBEBufPos = capacity;
    167             } else if (preallocatedBEBuf.capacity() - preallocatedBEBufPos >= capacity) {
    168                 slice = preallocatedBEBuf.slice(preallocatedBEBufPos, capacity);
    169                 preallocatedBEBufPos += capacity;
    170             } else {
    171                 preallocatedBEBuf = ChannelBuffers.directBuffer(ByteOrder.BIG_ENDIAN, preallocatedBufCapacity);
    172                 slice = preallocatedBEBuf.slice(0, capacity);
    173                 preallocatedBEBufPos = capacity;
    174             }
    175         }
    176         return slice;
    177     }
    178
    179     private ChannelBuffer allocateLittleEndianBuffer(int capacity) {
    180         ChannelBuffer slice;
    181         synchronized (littleEndianLock) {
    182             if (preallocatedLEBuf == null) {
    183                 preallocatedLEBuf = ChannelBuffers.directBuffer(ByteOrder.LITTLE_ENDIAN, preallocatedBufCapacity);
    184                 slice = preallocatedLEBuf.slice(0, capacity);
    185                 preallocatedLEBufPos = capacity;
    186             } else if (preallocatedLEBuf.capacity() - preallocatedLEBufPos >= capacity) {
    187                 slice = preallocatedLEBuf.slice(preallocatedLEBufPos, capacity);
    188                 preallocatedLEBufPos += capacity;
    189             } else {
    190                 preallocatedLEBuf = ChannelBuffers.directBuffer(ByteOrder.LITTLE_ENDIAN, preallocatedBufCapacity);
    191                 slice = preallocatedLEBuf.slice(0, capacity);
    192                 preallocatedLEBufPos = capacity;
    193             }
    194         }
    195         return slice;
    196     }
    197 }