001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.util;
016    
017    import java.lang.ref.ReferenceQueue;
018    import java.lang.ref.SoftReference;
019    
020    import java.util.ArrayList;
021    import java.util.Collections;
022    import java.util.List;
023    import java.util.concurrent.locks.Lock;
024    import java.util.concurrent.locks.ReentrantLock;
025    
026    /**
027     * @author Shuyang Zhou
028     */
029    public class CharBufferPool {
030    
031            public static char[] borrow(int size) {
032                    if (!isEnabled()) {
033                            return new char[size];
034                    }
035    
036                    _cleanUpDeadBuffers();
037    
038                    int poolSize = -1;
039    
040                    _modifyLock.lock();
041    
042                    try {
043                            int index = Collections.binarySearch(
044                                    _charBufferHoldersPool, new CharBufferHolder(size));
045    
046                            if (index < 0) {
047                                    index = -(index + 1);
048                            }
049    
050                            while (index < _charBufferHoldersPool.size()) {
051                                    CharBufferHolder charBufferHolder = _charBufferHoldersPool.get(
052                                            index);
053    
054                                    if (charBufferHolder._borrowed) {
055                                            index++;
056                                    }
057                                    else {
058                                            char[] charBuffer = charBufferHolder.get();
059    
060                                            if (charBuffer != null) {
061                                                    charBufferHolder._borrowed = true;
062    
063                                                    List<CharBufferHolder> borrowedCharBufferHolders =
064                                                            _borrowedCharBufferHoldersThreadLocal.get();
065    
066                                                    borrowedCharBufferHolders.add(charBufferHolder);
067    
068                                                    return charBuffer;
069                                            }
070    
071                                            _charBufferHoldersPool.remove(index);
072                                    }
073                            }
074                    }
075                    finally {
076                            poolSize = _charBufferHoldersPool.size();
077    
078                            _modifyLock.unlock();
079                    }
080    
081                    char[] charBuffer = new char[size + (size >> 9)];
082    
083                    if (poolSize < _MAX_POOL_SIZE) {
084                            CharBufferHolder charBufferHolder = new CharBufferHolder(
085                                    charBuffer);
086    
087                            List<CharBufferHolder> borrowedCharBufferHolders =
088                                    _borrowedCharBufferHoldersThreadLocal.get();
089    
090                            borrowedCharBufferHolders.add(charBufferHolder);
091                    }
092    
093                    return charBuffer;
094            }
095    
096            public static void cleanUp() {
097                    List<CharBufferHolder> charBufferHolders =
098                            _borrowedCharBufferHoldersThreadLocal.get();
099    
100                    _modifyLock.lock();
101    
102                    try {
103                            for (CharBufferHolder charBufferHolder : charBufferHolders) {
104                                    if (charBufferHolder._borrowed) {
105                                            charBufferHolder._borrowed = false;
106                                    }
107                                    else {
108                                            int index = Collections.binarySearch(
109                                                    _charBufferHoldersPool, charBufferHolder);
110    
111                                            if (index < 0) {
112                                                    index = -(index + 1);
113                                            }
114    
115                                            _charBufferHoldersPool.add(index, charBufferHolder);
116                                    }
117                            }
118                    }
119                    finally {
120                            _modifyLock.unlock();
121                    }
122    
123                    charBufferHolders.clear();
124    
125                    _cleanUpDeadBuffers();
126            }
127    
128            public static boolean isEnabled() {
129                    return _enabledThreadLocal.get();
130            }
131    
132            public static void setEnabled(boolean enabled) {
133                    _enabledThreadLocal.set(enabled);
134            }
135    
136            private static void _cleanUpDeadBuffers() {
137    
138                    // Peek before acquiring a Lock. This is crucial for concurrency since
139                    // SoftReferences will only be freed when there is a full GC or CMS
140                    // rescan. This means that the ReferenceQueue will be empty most of time
141                    // and should return immediately without touching the Lock. But when the
142                    // ReferenceQueue is not empty because a SoftReference has been freed by
143                    // the GC, it is more efficient to hold the Lock outside the while loop
144                    // rather than acquiring many dead CharBufferHolders.
145    
146                    CharBufferHolder charBufferHolder =
147                            (CharBufferHolder)_referenceQueue.poll();
148    
149                    if (charBufferHolder == null) {
150                            return;
151                    }
152    
153                    _modifyLock.lock();
154    
155                    try {
156                            do {
157                                    _charBufferHoldersPool.remove(charBufferHolder);
158                            }
159                            while ((charBufferHolder =
160                                                    (CharBufferHolder)_referenceQueue.poll()) != null);
161                    }
162                    finally {
163                            _modifyLock.unlock();
164                    }
165            }
166    
167            /**
168             * The initial pool size should be set slightly higher than the maximum real
169             * world concurrent processing request number. This value should not be
170             * tuned because it is based on extensive performance tests that tie to the
171             * overall system's inherent nature.
172             */
173            private static final int _INITIAL_POOL_SIZE = 50;
174    
175            /**
176             * Make the actual maximum pool size twice the initial pool size to prevent
177             * random peaks that may cause an unnecessarily high use of old generation
178             * memory.
179             */
180            private static final int _MAX_POOL_SIZE = _INITIAL_POOL_SIZE * 2;
181    
182            private static ThreadLocal<List<CharBufferHolder>>
183                    _borrowedCharBufferHoldersThreadLocal =
184                            new AutoResetThreadLocal<List<CharBufferHolder>>(
185                                    CharBufferPool.class.getName() +
186                                            "._borrowedCharBufferHoldersThreadLocal",
187                                    new ArrayList<CharBufferHolder>());
188            private static List<CharBufferHolder> _charBufferHoldersPool =
189                    new ArrayList<CharBufferHolder>(_INITIAL_POOL_SIZE);
190            private static ThreadLocal<Boolean> _enabledThreadLocal =
191                    new AutoResetThreadLocal<Boolean>(
192                            CharBufferPool.class.getName() + "._enabledThreadLocal", false);
193            private static Lock _modifyLock = new ReentrantLock();
194            private static ReferenceQueue<Object> _referenceQueue =
195                    new ReferenceQueue<Object>();
196    
197            private static class CharBufferHolder
198                    extends SoftReference<char[]> implements Comparable<CharBufferHolder> {
199    
200                    public CharBufferHolder(char[] charBuffer) {
201                            super(charBuffer, _referenceQueue);
202    
203                            _length = charBuffer.length;
204                    }
205    
206                    public CharBufferHolder(int length) {
207                            super(null);
208    
209                            _length = length;
210                    }
211    
212                    @Override
213                    public int compareTo(CharBufferHolder charBufferHolder) {
214                            return _length - charBufferHolder._length;
215                    }
216    
217                    private boolean _borrowed;
218                    private int _length;
219    
220            }
221    
222    }