001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: * The Original Software is NetBeans. The Initial Developer of the Original
026: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
027: * Microsystems, Inc. All Rights Reserved.
028: *
029: * If you wish your version of this file to be governed by only the CDDL
030: * or only the GPL Version 2, indicate your decision by adding
031: * "[Contributor] elects to include this software in this distribution
032: * under the [CDDL or GPL Version 2] license." If you do not indicate a
033: * single choice of license, a recipient has the option to distribute
034: * your version of this file under either the CDDL, the GPL Version 2 or
035: * to extend the choice of license to its licensees as provided above.
036: * However, if you add GPL Version 2 code and therefore, elected the GPL
037: * Version 2 license, then the option applies only if the new code is
038: * made subject to such option by the copyright holder.
039: */
040:
041: package org.netbeans.lib.profiler.heap;
042:
043: import java.io.File;
044: import java.io.FileNotFoundException;
045: import java.io.IOException;
046: import java.io.RandomAccessFile;
047: import java.nio.ByteBuffer;
048: import java.nio.MappedByteBuffer;
049: import java.nio.channels.FileChannel;
050:
051: /**
052: * key - ID (long/int) of heap object
053: * value 8+4+8 = 20 bytes
054: * - offset (long/int) to dump file
055: * - instance index (int) - unique number of this {@link Instance} among all instances of the same Java Class
056: * - ID (long/int) to nearest GC root, 0 for GC root or if is not computed yet
057: *
058: * @author Tomas Hurka
059: */
060: class LongMap {
061: //~ Inner Interfaces ---------------------------------------------------------------------------------------------------------
062:
063: interface Data {
064: //~ Methods --------------------------------------------------------------------------------------------------------------
065:
066: int getInt(long index);
067:
068: long getLong(long index);
069:
070: void putInt(long index, int data);
071:
072: void putLong(long index, long data);
073: }
074:
075: //~ Inner Classes ------------------------------------------------------------------------------------------------------------
076:
077: class Entry {
078: //~ Instance fields ------------------------------------------------------------------------------------------------------
079:
080: private long offset;
081:
082: //~ Constructors ---------------------------------------------------------------------------------------------------------
083:
084: private Entry(long off) {
085: offset = off;
086: }
087:
088: //~ Methods --------------------------------------------------------------------------------------------------------------
089:
090: void setIndex(int index) {
091: dumpBuffer.putInt(offset + KEY_SIZE + FOFFSET_SIZE, index);
092: }
093:
094: int getIndex() {
095: return dumpBuffer.getInt(offset + KEY_SIZE + FOFFSET_SIZE);
096: }
097:
098: void setNearestGCRootPointer(long instanceId) {
099: putID(offset + KEY_SIZE + FOFFSET_SIZE + 4, instanceId);
100: }
101:
102: long getNearestGCRootPointer() {
103: return getID(offset + KEY_SIZE + FOFFSET_SIZE + 4);
104: }
105:
106: long getOffset() {
107: return getFoffset(offset + KEY_SIZE);
108: }
109: }
110:
111: class FileData implements Data {
112: //~ Instance fields ------------------------------------------------------------------------------------------------------
113:
114: RandomAccessFile file;
115: byte[] buf;
116: boolean bufferModified;
117: long offset;
118: final int BUFFER_SIZE = 128;
119:
120: //~ Constructors ---------------------------------------------------------------------------------------------------------
121:
122: FileData(RandomAccessFile f, long length) throws IOException {
123: file = f;
124: buf = new byte[ENTRY_SIZE * BUFFER_SIZE];
125: }
126:
127: //~ Methods --------------------------------------------------------------------------------------------------------------
128:
129: public synchronized int getInt(long index) {
130: int i = loadBufferIfNeeded(index);
131: int ch1 = ((int) buf[i++]) & 0xFF;
132: int ch2 = ((int) buf[i++]) & 0xFF;
133: int ch3 = ((int) buf[i++]) & 0xFF;
134: int ch4 = ((int) buf[i]) & 0xFF;
135:
136: return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
137: }
138:
139: public synchronized long getLong(long index) {
140: int i = loadBufferIfNeeded(index);
141: return (((long) buf[i++] << 56)
142: + ((long) (buf[i++] & 255) << 48)
143: + ((long) (buf[i++] & 255) << 40)
144: + ((long) (buf[i++] & 255) << 32)
145: + ((long) (buf[i++] & 255) << 24)
146: + ((buf[i++] & 255) << 16)
147: + ((buf[i++] & 255) << 8) + ((buf[i++] & 255) << 0));
148: }
149:
150: public synchronized void putInt(long index, int data) {
151: int i = loadBufferIfNeeded(index);
152: buf[i++] = (byte) (data >>> 24);
153: buf[i++] = (byte) (data >>> 16);
154: buf[i++] = (byte) (data >>> 8);
155: buf[i++] = (byte) (data >>> 0);
156: bufferModified = true;
157: }
158:
159: public synchronized void putLong(long index, long data) {
160: int i = loadBufferIfNeeded(index);
161: buf[i++] = (byte) (data >>> 56);
162: buf[i++] = (byte) (data >>> 48);
163: buf[i++] = (byte) (data >>> 40);
164: buf[i++] = (byte) (data >>> 32);
165: buf[i++] = (byte) (data >>> 24);
166: buf[i++] = (byte) (data >>> 16);
167: buf[i++] = (byte) (data >>> 8);
168: buf[i++] = (byte) (data >>> 0);
169: bufferModified = true;
170: }
171:
172: private int loadBufferIfNeeded(long index) {
173: int i = (int) (index % (ENTRY_SIZE * BUFFER_SIZE));
174: long newOffset = index - i;
175:
176: if (offset != newOffset) {
177: try {
178: if (bufferModified) {
179: file.seek(offset);
180: file.write(buf, 0, getBufferSize(offset));
181: bufferModified = false;
182: }
183:
184: file.seek(newOffset);
185: file.readFully(buf, 0, getBufferSize(newOffset));
186: } catch (IOException ex) {
187: ex.printStackTrace();
188: }
189:
190: offset = newOffset;
191: }
192:
193: return i;
194: }
195:
196: private int getBufferSize(long off) {
197: int size = buf.length;
198:
199: if (fileSize - off < buf.length) {
200: size = (int) (fileSize - off);
201: }
202: return size;
203: }
204: }
205:
206: class MemoryMappedData implements Data {
207: //~ Instance fields ------------------------------------------------------------------------------------------------------
208:
209: MappedByteBuffer buf;
210:
211: //~ Constructors ---------------------------------------------------------------------------------------------------------
212:
213: MemoryMappedData(RandomAccessFile file, long length)
214: throws IOException {
215: FileChannel channel = file.getChannel();
216: buf = channel
217: .map(FileChannel.MapMode.READ_WRITE, 0, length);
218: channel.close();
219: }
220:
221: //~ Methods --------------------------------------------------------------------------------------------------------------
222:
223: public int getInt(long index) {
224: return buf.getInt((int) index);
225: }
226:
227: public long getLong(long index) {
228: return buf.getLong((int) index);
229: }
230:
231: public void putInt(long index, int data) {
232: buf.putInt((int) index, data);
233: }
234:
235: public void putLong(long index, long data) {
236: buf.putLong((int) index, data);
237: }
238: }
239:
240: //~ Instance fields ----------------------------------------------------------------------------------------------------------
241:
242: File tempFile;
243: private final int KEY_SIZE;
244: private final int VALUE_SIZE;
245: private final int ENTRY_SIZE;
246: private final int ID_SIZE;
247: private final int FOFFSET_SIZE;
248: private Data dumpBuffer;
249: private long fileSize;
250: private long keys;
251:
252: //~ Constructors -------------------------------------------------------------------------------------------------------------
253:
254: LongMap(int size, int idSize, int foosetSize)
255: throws FileNotFoundException, IOException {
256: assert idSize == 4 || idSize == 8;
257: assert foosetSize == 4 || foosetSize == 8;
258: keys = (size * 4L) / 3L;
259: ID_SIZE = idSize;
260: FOFFSET_SIZE = foosetSize;
261: KEY_SIZE = ID_SIZE;
262: VALUE_SIZE = FOFFSET_SIZE + 4 + ID_SIZE;
263: ENTRY_SIZE = KEY_SIZE + VALUE_SIZE;
264: fileSize = keys * ENTRY_SIZE;
265: tempFile = File.createTempFile("NBProfiler", ".map"); // NOI18N
266: byte[] zeros = new byte[512 * 1024];
267:
268: RandomAccessFile file = new RandomAccessFile(tempFile, "rw"); // NOI18N
269: while (file.length() < fileSize) {
270: file.write(zeros);
271: }
272: file.setLength(fileSize);
273: setDumpBuffer(file);
274: tempFile.deleteOnExit();
275: }
276:
277: //~ Methods ------------------------------------------------------------------------------------------------------------------
278:
279: protected void finalize() throws Throwable {
280: tempFile.delete();
281: super .finalize();
282: }
283:
284: Entry get(long key) {
285: long index = getIndex(key);
286:
287: while (true) {
288: long mapKey = getID(index);
289:
290: if (mapKey == key) {
291: return new Entry(index);
292: }
293:
294: if (mapKey == 0L) {
295: return null;
296: }
297:
298: index = getNextIndex(index);
299: }
300: }
301:
302: void put(long key, long value) {
303: long index = getIndex(key);
304:
305: while (true) {
306: if (getID(index) == 0L) {
307: putID(index, key);
308: putFoffset(index + KEY_SIZE, value);
309:
310: return;
311: }
312:
313: index = getNextIndex(index);
314: }
315: }
316:
317: private void setDumpBuffer(RandomAccessFile file)
318: throws IOException {
319: long length = file.length();
320:
321: try {
322: if (length > Integer.MAX_VALUE) {
323: dumpBuffer = new FileData(file, length);
324: } else {
325: dumpBuffer = new MemoryMappedData(file, length);
326: }
327: } catch (IOException ex) {
328: if (ex.getCause() instanceof OutOfMemoryError) {
329: dumpBuffer = new FileData(file, length);
330: } else {
331: throw ex;
332: }
333: }
334: }
335:
336: private long getID(long index) {
337: if (ID_SIZE == 4) {
338: return dumpBuffer.getInt(index);
339: }
340: return dumpBuffer.getLong(index);
341: }
342:
343: private void putID(long index, long key) {
344: if (ID_SIZE == 4) {
345: dumpBuffer.putInt(index, (int) key);
346: } else {
347: dumpBuffer.putLong(index, key);
348: }
349: }
350:
351: private long getFoffset(long index) {
352: if (FOFFSET_SIZE == 4) {
353: return dumpBuffer.getInt(index);
354: }
355: return dumpBuffer.getLong(index);
356: }
357:
358: private void putFoffset(long index, long key) {
359: if (FOFFSET_SIZE == 4) {
360: dumpBuffer.putInt(index, (int) key);
361: } else {
362: dumpBuffer.putLong(index, key);
363: }
364: }
365:
366: private long getIndex(long key) {
367: long hash = key & 0x7FFFFFFFFFFFFFFFL;
368: return (hash % keys) * ENTRY_SIZE;
369: }
370:
371: private long getNextIndex(long index) {
372: index += ENTRY_SIZE;
373: if (index >= fileSize) {
374: index = 0;
375: }
376:
377: return index;
378: }
379: }
|