001    /**
002     * Copyright (c) 2000-2011 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
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 :
104                                            charBufferHolders) {
105    
106                                    if (charBufferHolder._borrowed) {
107                                            charBufferHolder._borrowed = false;
108                                    }
109                                    else {
110                                            int index = Collections.binarySearch(
111                                                    _charBufferHoldersPool, charBufferHolder);
112    
113                                            if (index < 0) {
114                                                    index = -(index + 1);
115                                            }
116    
117                                            _charBufferHoldersPool.add(index, charBufferHolder);
118                                    }
119                            }
120                    }
121                    finally {
122                            _modifyLock.unlock();
123                    }
124    
125                    charBufferHolders.clear();
126    
127                    _cleanUpDeadBuffers();
128            }
129    
130            public static boolean isEnabled() {
131                    return _enabledThreadLocal.get();
132            }
133    
134            public static void setEnabled(boolean enabled) {
135                    _enabledThreadLocal.set(enabled);
136            }
137    
138            private static void _cleanUpDeadBuffers() {
139    
140                    // Peek before acquiring a Lock. This is crucial for concurrency since
141                    // SoftReferences will only be freed when there is a full GC or CMS
142                    // rescan. This means that the ReferenceQueue will be empty most of time
143                    // and should return immediately without touching the Lock. But when the
144                    // ReferenceQueue is not empty because a SoftReference has been freed by
145                    // the GC, it is more efficient to hold the Lock outside the while loop
146                    // rather than acquiring many dead CharBufferHolders.
147    
148                    CharBufferHolder charBufferHolder =
149                            (CharBufferHolder)_referenceQueue.poll();
150    
151                    if (charBufferHolder == null) {
152                            return;
153                    }
154    
155                    _modifyLock.lock();
156    
157                    try {
158                            do {
159                                    _charBufferHoldersPool.remove(charBufferHolder);
160                            }
161                            while ((charBufferHolder =
162                                                    (CharBufferHolder)_referenceQueue.poll()) != null);
163                    }
164                    finally {
165                            _modifyLock.unlock();
166                    }
167            }
168    
169            /**
170             * The initial pool size should be set slightly higher than the maximum real
171             * world concurrent processing request number. This value should not be
172             * tuned because it is based on extensive performance tests that tie to the
173             * overall system's inherent nature.
174             */
175            private static final int _INITIAL_POOL_SIZE = 50;
176    
177            /**
178             * Make the actual maximum pool size twice the initial pool size to prevent
179             * random peaks that may cause an unnecessarily high use of old generation
180             * memory.
181             */
182            private static final int _MAX_POOL_SIZE = _INITIAL_POOL_SIZE * 2;
183    
184            private static ThreadLocal<List<CharBufferHolder>>
185                    _borrowedCharBufferHoldersThreadLocal =
186                            new AutoResetThreadLocal<List<CharBufferHolder>>(
187                                    CharBufferPool.class.getName()
188                                            + "._borrowedCharBufferHoldersThreadLocal",
189                                    new ArrayList<CharBufferHolder>());
190            private static List<CharBufferHolder> _charBufferHoldersPool =
191                    new ArrayList<CharBufferHolder>(_INITIAL_POOL_SIZE);
192            private static ThreadLocal<Boolean> _enabledThreadLocal =
193                    new AutoResetThreadLocal<Boolean>(
194                            CharBufferPool.class.getName() + "._enabledThreadLocal", false);
195            private static Lock _modifyLock = new ReentrantLock();
196            private static ReferenceQueue<Object> _referenceQueue =
197                    new ReferenceQueue<Object>();
198    
199            private static class CharBufferHolder
200                    extends SoftReference<char[]> implements Comparable<CharBufferHolder> {
201    
202                    public CharBufferHolder(char[] charBuffer) {
203                            super(charBuffer, _referenceQueue);
204    
205                            _length = charBuffer.length;
206                    }
207    
208                    public CharBufferHolder(int length) {
209                            super(null);
210    
211                            _length = length;
212                    }
213    
214                    public int compareTo(CharBufferHolder charBufferHolder) {
215                            return _length - charBufferHolder._length;
216                    }
217    
218                    private boolean _borrowed;
219                    private int _length;
220    
221            }
222    
223    }