├── .gitignore ├── LICENSE ├── README.md ├── core ├── pom.xml └── src │ ├── main │ └── java │ │ └── org │ │ └── adoptopenjdk │ │ └── jheappo │ │ ├── io │ │ ├── AllocSites.java │ │ ├── CPUSamples.java │ │ ├── ControlSettings.java │ │ ├── EncodedChunk.java │ │ ├── EndThread.java │ │ ├── HeapDumpEnd.java │ │ ├── HeapDumpSegment.java │ │ ├── HeapProfile.java │ │ ├── HeapProfileHeader.java │ │ ├── HeapProfileRecord.java │ │ ├── HeapSummary.java │ │ ├── LoadClass.java │ │ ├── StackFrame.java │ │ ├── StackTrace.java │ │ ├── StartThread.java │ │ ├── UTF8StringSegment.java │ │ └── UnloadClass.java │ │ ├── model │ │ ├── ArrayValue.java │ │ ├── BasicDataTypeValue.java │ │ ├── BooleanValue.java │ │ ├── ByteValue.java │ │ ├── CharValue.java │ │ ├── DoubleValue.java │ │ ├── FloatValue.java │ │ ├── IntValue.java │ │ ├── LongValue.java │ │ ├── ObjectValue.java │ │ ├── PrimitiveValue.java │ │ ├── ShortValue.java │ │ └── UnknownValue.java │ │ └── objects │ │ ├── BasicDataTypes.java │ │ ├── ClassObject.java │ │ ├── HeapObject.java │ │ ├── InstanceObject.java │ │ ├── ObjectArray.java │ │ ├── PrimitiveArray.java │ │ ├── RootJNIGlobal.java │ │ ├── RootJNILocal.java │ │ ├── RootJavaFrame.java │ │ ├── RootMonitorUsed.java │ │ ├── RootNativeStack.java │ │ ├── RootStickyClass.java │ │ ├── RootThreadBlock.java │ │ ├── RootThreadObject.java │ │ ├── RootUnknown.java │ │ └── UTF8String.java │ └── test │ └── java │ └── org │ └── adoptopenjdk │ └── jheappo │ └── io │ └── HeapDumpSectionTest.kt ├── data ├── HeapDumpSegment.hprof └── mastermind.hprof ├── pom.xml └── tools ├── pom.xml └── src ├── main └── java │ └── org │ └── adoptopenjdk │ └── jheappo │ ├── Heappo.java │ └── model │ ├── HeapGraph.java │ ├── HeapGraphExtras.java │ └── JavaHeap.java └── test ├── java └── org │ └── adoptopenjdk │ └── jheappo │ └── heaptextoutput │ └── HeapDumpTextTest.kt └── resources └── org └── adoptopenjdk └── jheappo └── heaptextoutput ├── class.table ├── heap.dump ├── instance.table ├── loadClass.table └── string.table /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX files 2 | *DS_Store 3 | 4 | #IDE 5 | .idea/* 6 | *.iml 7 | 8 | # Compiled class file 9 | *.class 10 | 11 | # Log file 12 | *.log 13 | 14 | # BlueJ files 15 | *.ctxt 16 | 17 | # Mobile Tools for Java (J2ME) 18 | .mtj.tmp/ 19 | 20 | # Package Files # 21 | *.jar 22 | *.war 23 | *.ear 24 | *.zip 25 | *.tar.gz 26 | *.rar 27 | 28 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 29 | hs_err_pid* 30 | 31 | /target 32 | */target 33 | dependency-reduced-pom.xml 34 | 35 | /tmp 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jheappo 2 | A Heap Dump Parser 3 | 4 | 5 | ## Neo4j Export 6 | 7 | run Heappo and add the "graph" parameter 8 | 9 | `java -jar path/to/jheappo.jar "/path/to/file.hprof" graph` 10 | 11 | It will create a `graph.db` directory. 12 | 13 | The graph model is: 14 | 15 | ``` 16 | (:Class {id, name, _fields})-[:SUPERCLASS]->(:Class) 17 | (:Instance {id, _fields})-[:IS_CLASS]->(:Class) 18 | (:Instance)-[:CONTAINS]->(:Instance) 19 | ``` 20 | 21 | 22 | ### Query Graph Console 23 | 24 | You can open it directly with neo4j-shell: 25 | 26 | `$NEO4J_HOME/bin/neo4j-shell -path graph.db` 27 | 28 | and then run queries like: 29 | 30 | ``` 31 | ~/v/neo4j-enterprise-3.3.3/bin/neo4j-shell -path graph.db 32 | NOTE: Local Neo4j graph database service at 'graph.db' 33 | 34 | match (c:Class) return c limit 5; 35 | +---------------------------------------------------------------------------------------------------------------------------------------------------+ 36 | | c | 37 | +---------------------------------------------------------------------------------------------------------------------------------------------------+ 38 | | Node[0]{id:28991131920,name:"sun/lwawt/macosx/CPlatformWindow$$Lambda$33",size:8,_arg$1:2} | 39 | | Node[1]{size:0,id:28991103008,name:"java/lang/Object"} | 40 | | Node[2]{id:28991132048,name:"sun/java2d/opengl/CGLLayer$$Lambda$32",size:0} | 41 | | Node[3]{id:28991132200,name:"java/lang/invoke/LambdaForm$MH",size:0} | 42 | | Node[4]{id:28991132328,name:"java/lang/invoke/LambdaForm$DMH",size:0} | 43 | | Node[5]{id:28991132456,name:"java/lang/invoke/LambdaForm$DMH",size:0} | 44 | | Node[6]{_arg$5:2,_arg$4:2,_arg$3:2,_arg$2:10,id:28991132584,name:"com/apple/laf/AquaPainter$AquaSingleImagePainter$$Lambda$31",size:32,_arg$1:10} | 45 | +---------------------------------------------------------------------------------------------------------------------------------------------------+ 46 | 47 | match (c:Class) where not c.name starts with "java/lang/invoke/LambdaForm" return c limit 100; 48 | 49 | match (c:Class) where c.name = "java/lang/Object" return c; 50 | 51 | match (c:Class) where c.name = "java/lang/Object" return c, size( (c)<-[:IS_CLASS]-() ); 52 | 53 | match (c:Instance) return count(*); 54 | +----------+ 55 | | count(*) | 56 | +----------+ 57 | | 38359 | 58 | +----------+ 59 | 1 row 60 | 70 ms 61 | match (c:Instance) where not (c)<-[:CONTAINS]-() return count(*); 62 | +----------+ 63 | | count(*) | 64 | +----------+ 65 | | 14611 | 66 | +----------+ 67 | 1 row 68 | 213 ms 69 | 70 | match (i:Instance)-[:IS_CLASS]->(c) where not (i)<-[:CONTAINS]-() return c.name, i limit 10;; 71 | +------------------------------------------------------------------------------------------------+ 72 | | c.name | i | 73 | +------------------------------------------------------------------------------------------------+ 74 | | "java/util/concurrent/locks/ReentrantLock" | Node[2188]{stackSerial:1,id:28991029248} | 75 | | "java/lang/String" | Node[2190]{stackSerial:1,id:28991029264,_hash:0} | 76 | | "java/lang/String" | Node[2195]{stackSerial:1,id:28991030104,_hash:0} | 77 | | "java/lang/String" | Node[2197]{stackSerial:1,id:28991030672,_hash:0} | 78 | | "java/lang/String" | Node[2199]{stackSerial:1,id:28991030696,_hash:0} | 79 | | "java/lang/String" | Node[2201]{stackSerial:1,id:28991030720,_hash:0} | 80 | | "java/lang/StringBuilder" | Node[2203]{stackSerial:1,id:28991031944} | 81 | | "java/lang/String" | Node[2204]{stackSerial:1,id:28991031968,_hash:0} | 82 | | "java/lang/String" | Node[2206]{stackSerial:1,id:28991031992,_hash:0} | 83 | | "sun/awt/SunHints$Value" | Node[2213]{stackSerial:1,id:28991032064,_index:2} | 84 | +------------------------------------------------------------------------------------------------+ 85 | 10 rows 86 | 52 ms 87 | 88 | 89 | match (c:Class) return c.name, size( (c)<-[:IS_CLASS]-() ) as instances order by instances desc limit 10; 90 | +-----------------------------------------------------------+ 91 | | c.name | instances | 92 | +-----------------------------------------------------------+ 93 | | "java/lang/String" | 7968 | 94 | | "java/util/Hashtable$Entry" | 1805 | 95 | | "java/util/concurrent/ConcurrentHashMap$Node" | 1348 | 96 | | "java/util/HashMap$Node" | 1323 | 97 | | "java/lang/ref/SoftReference" | 1123 | 98 | | "java/util/concurrent/ConcurrentHashMap" | 838 | 99 | | "java/lang/ref/Finalizer" | 815 | 100 | | "sun/font/Font2DHandle" | 793 | 101 | | "sun/font/CFont" | 785 | 102 | | "java/lang/reflect/Field" | 561 | 103 | +-----------------------------------------------------------+ 104 | 10 rows 105 | 12 ms 106 | ``` 107 | 108 | ### Graph Visualization 109 | 110 | Ìf you want to see the graph visually, download and install Neo4j(Desktop) and create an new "Graph" in your "Project" 111 | 112 | Then go to "Manager" -> "Open Folder" and copy the `graph.db` directory into `data/database/graph.db` 113 | 114 | Then you can start the server and "Open Neo4j Browser" to execute queries and interactively visualize the data. 115 | 116 | Note: As the Heap graph is very large and dense, Neo4j browser's default visualization will come to its limits quickly. 117 | 118 | Then you need to add LIMIT's or filter / group data. 119 | 120 | -------------------------------------------------------------------------------- /core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | jheappo 7 | jheappo-parent 8 | 1.0.0-SNAPSHOT 9 | 10 | 11 | jheappo-core 12 | 13 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/io/AllocSites.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.io; 2 | 3 | 4 | public class AllocSites extends HeapProfileRecord { 5 | public final static int TAG = 0x06; 6 | 7 | public AllocSites(EncodedChunk body) { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/io/CPUSamples.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.io; 2 | 3 | 4 | public class CPUSamples extends HeapProfileRecord { 5 | public static final int TAG = 0x0D; 6 | 7 | public CPUSamples(EncodedChunk body) { } 8 | } 9 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/io/ControlSettings.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.io; 2 | 3 | 4 | public class ControlSettings extends HeapProfileRecord { 5 | public static final int TAG = 0x0E; 6 | 7 | public ControlSettings(EncodedChunk body) { 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/io/EncodedChunk.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.io; 2 | 3 | import java.io.PrintStream; 4 | import java.util.stream.IntStream; 5 | 6 | import org.adoptopenjdk.jheappo.model.*; 7 | import org.adoptopenjdk.jheappo.objects.BasicDataTypes; 8 | 9 | /** 10 | * A wrapper around bytes that represents the encoding used by the hprof binary format. 11 | *

12 | * This is used by various hprof record types representing stack frames, objects in the heap, etc to decode the 13 | * corresponding components (e.g. id numbers). Notably, most things are represented as unsigned ints, which we cannot 14 | * represent well (yet). 15 | *

16 | * See https://hg.openjdk.java.net/jdk/jdk/file/9a73a4e4011f/src/hotspot/share/services/heapDumper.cpp for details on 17 | * the format. 18 | */ 19 | public class EncodedChunk { 20 | private final byte[] body; 21 | private int index; 22 | 23 | public EncodedChunk(byte[] body) { 24 | this(body, 0); 25 | } 26 | 27 | private EncodedChunk(byte[] body, int index) { 28 | this.body = body; 29 | this.index = index; 30 | } 31 | 32 | public byte[] read(int bufferLength) { 33 | byte[] buffer = new byte[bufferLength]; 34 | for (int i = 0; i < bufferLength; i++) { 35 | buffer[i] = (byte) (body[index++] & 0xff); 36 | } 37 | 38 | return buffer; 39 | } 40 | 41 | protected byte[] getBody() { 42 | 43 | return body; 44 | } 45 | 46 | public void skip(int skipOver) { 47 | 48 | index += skipOver; 49 | } 50 | 51 | public byte[] readRemaining() { 52 | 53 | return read(body.length - index); 54 | } 55 | 56 | public int getIndex() { 57 | return index; 58 | } 59 | 60 | public boolean endOfBuffer() { 61 | 62 | return getBody().length <= index; 63 | } 64 | 65 | private int read() { 66 | 67 | return body[index++] & 0xff; 68 | } 69 | 70 | public byte extractU1() { 71 | 72 | return (byte) read(); 73 | } 74 | 75 | public short extractU2() { 76 | int value = extractU1(); 77 | 78 | for (int cursor = 1; cursor < 2; cursor++) { 79 | value = (value << 8) | (short) read(); 80 | } 81 | 82 | return (short) value; 83 | } 84 | 85 | public int extractU4() { 86 | int value = extractU1(); 87 | for (int cursor = 1; cursor < 4; cursor++) { 88 | value = (value << 8) | read(); 89 | } 90 | 91 | return value; 92 | } 93 | 94 | public long extractU8() { 95 | long value = extractU1(); 96 | for (int cursor = 1; cursor < 8; cursor++) { 97 | value = (value << 8) | (long) read(); 98 | } 99 | 100 | return value; 101 | } 102 | 103 | public long extractID() { 104 | 105 | return extractU8(); 106 | } 107 | 108 | public boolean extractBoolean() { 109 | 110 | return extractU1() != 0; 111 | } 112 | 113 | public char extractChar() { 114 | 115 | return (char) extractU2(); 116 | } 117 | 118 | public byte extractByte() { 119 | 120 | return extractU1(); 121 | } 122 | 123 | public short extractShort() { 124 | 125 | return extractU2(); 126 | } 127 | 128 | public float extractFloat() { 129 | 130 | return Float.intBitsToFloat(extractInt()); 131 | } 132 | 133 | public int extractInt() { 134 | 135 | return extractU4(); 136 | } 137 | 138 | public double extractDouble() { 139 | 140 | return Double.longBitsToDouble(extractLong()); 141 | } 142 | 143 | public long extractLong() { 144 | return extractU8(); 145 | } 146 | 147 | public BasicDataTypeValue extractBasicType(BasicDataTypes basicType) { 148 | 149 | switch (basicType) { 150 | case BOOLEAN: 151 | 152 | return new BooleanValue(this.extractBoolean()); 153 | case CHAR: 154 | 155 | return new CharValue(this.extractChar()); 156 | case BYTE: 157 | 158 | return new ByteValue(this.extractByte()); 159 | case SHORT: 160 | 161 | return new ShortValue(this.extractShort()); 162 | case FLOAT: 163 | 164 | return new FloatValue(this.extractFloat()); 165 | case INT: 166 | 167 | return new IntValue(this.extractInt()); 168 | case OBJECT: 169 | 170 | return new ObjectValue(this.extractID()); 171 | case DOUBLE: 172 | 173 | return new DoubleValue(this.extractDouble()); 174 | case LONG: 175 | 176 | return new LongValue(this.extractLong()); 177 | default: 178 | 179 | return new UnknownValue(); 180 | } 181 | } 182 | 183 | public BasicDataTypeValue extractBasicType(int basicType) { 184 | return extractBasicType(BasicDataTypes.fromInt(basicType)); 185 | } 186 | 187 | /** 188 | * Useful when you want to read some bytes without affecting the internal cursor position. 189 | * 190 | * @return A chunk with the same byte array and offset as this object. 191 | */ 192 | public EncodedChunk copy() { 193 | 194 | return new EncodedChunk(body, index); 195 | } 196 | 197 | /* 198 | Debugging aid 199 | */ 200 | public void dump(PrintStream out) { 201 | int max = (body.length > 1000) ? 1000 : body.length; 202 | IntStream.range(0, max) 203 | .forEach(cursor -> { 204 | System.out.print(Integer.toHexString(body[cursor] & 255)); 205 | System.out.print(" "); 206 | }); 207 | out.println(); 208 | IntStream.range(0, max) 209 | .forEach(cursor -> { 210 | System.out.print((char) (body[cursor] & 255)); 211 | System.out.print(" "); 212 | }); 213 | out.println(); 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/io/EndThread.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.io; 2 | 3 | 4 | public class EndThread extends HeapProfileRecord { 5 | public static final int TAG = 0x0B; 6 | 7 | public EndThread(EncodedChunk body){} 8 | } 9 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/io/HeapDumpEnd.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.io; 2 | 3 | 4 | public class HeapDumpEnd extends HeapProfileRecord { 5 | public static final int TAG = 0x2C; 6 | 7 | public HeapDumpEnd(EncodedChunk body) { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/io/HeapDumpSegment.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.io; 2 | 3 | 4 | import org.adoptopenjdk.jheappo.objects.*; 5 | 6 | /* 7 | ROOT UNKNOWN | 0xFF | ID | object ID 8 | 9 | ROOT JNI GLOBAL | 0x01 | ID | object ID 10 | | ID | JNI global ref ID 11 | 12 | ROOT JNI LOCAL | 0x02 | ID | object ID 13 | | u4 | thread serial number 14 | | u4 | frame number in stack trace (-1 for empty) 15 | 16 | ROOT JAVA FRAME | 0x03 | ID | object ID 17 | | u4 | thread serial number 18 | | u4 | frame number in stack trace (-1 for empty) 19 | 20 | ROOT NATIVE STACK | 0x04 | ID | object ID 21 | | u4 | thread serial number 22 | 23 | ROOT STICKY CLASS | 0x05 | ID | object ID 24 | 25 | ROOT THREAD BLOCK | 0x06 | ID | object ID 26 | | u4 | thread serial number 27 | 28 | ROOT MONITOR USED | 0x07 | ID | object ID 29 | 30 | ROOT THREAD OBJECT | 0x08 | ID | thread object ID 31 | | u4 | thread serial number 32 | | u4 | stack trace serial number 33 | 34 | CLASS DUMP | 0x20 | ID | class object ID 35 | | u4 | stack trace serial number 36 | | ID | super class object ID 37 | | ID | class loader object ID 38 | | ID | signers object ID 39 | | ID | protection domain object ID 40 | | ID | reserved 41 | | ID | reserved 42 | | u4 | instance size (in bytes) 43 | | u2 | size of constant pool and number of records that follow: 44 | | u2 | constant pool index 45 | | u1 | type of entry: (See Basic Type) 46 | | value | value of entry (u1, u2, u4, or u8 based on type of entry) 47 | | u2 | Number of static fields: 48 | | ID | static field name string ID 49 | | u1 | type of field: (See Basic Type) 50 | | value | value of entry (u1, u2, u4, or u8 based on type of field) 51 | | u2 | Number of instance fields (not including super class's) 52 | | ID | field name string ID 53 | | u1 | type of field: (See Basic Type) 54 | 55 | 56 | INSTANCE DUMP | 0x21 | ID | object ID 57 | | u4 | stack trace serial number 58 | | ID | class object ID 59 | | u4 | number of bytes that follow 60 | |[value]* | instance field values (this class, followed by super class, etc) 61 | 62 | OBJECT ARRAY DUMP | 0x22 | ID | array object ID 63 | | u4 | stack trace serial number 64 | | u4 | number of elements 65 | | ID | array class object ID 66 | | [ID]* | elements 67 | 68 | PRIMITIVE ARRAY DUMP | 0x23 | ID | array object ID 69 | | u4 | stack trace serial number 70 | | u4 | number of elements 71 | | u1 | element type (See Basic Type) 72 | | [u1]* | elements (packed array) 73 | 74 | 75 | 76 | Basic Types 77 | 78 | 2 | object 79 | 4 | boolean 80 | 5 | char 81 | 6 | float 82 | 7 | double 83 | 8 | byte 84 | 9 | short 85 | 10 | int 86 | 11 | long 87 | 88 | */ 89 | public class HeapDumpSegment extends HeapProfileRecord { 90 | public static final int TAG1 = 0x0C; 91 | public static final int TAG2 = 0x1C; 92 | 93 | private final EncodedChunk body; 94 | 95 | public HeapDumpSegment(EncodedChunk body) { 96 | this.body = body; 97 | } 98 | 99 | public boolean hasNext() { 100 | return body.endOfBuffer(); 101 | } 102 | 103 | public HeapObject next() { 104 | int typeCode = body.extractU1(); 105 | switch (typeCode) { 106 | case RootUnknown.TAG: 107 | return new RootUnknown(body); 108 | case RootJNIGlobal.TAG: 109 | return new RootJNIGlobal(body); 110 | case RootJNILocal.TAG: 111 | return new RootJNILocal(body); 112 | case RootJavaFrame.TAG: 113 | return new RootJavaFrame(body); 114 | case RootNativeStack.TAG: 115 | return new RootNativeStack(body); 116 | case RootStickyClass.TAG: 117 | return new RootStickyClass(body); 118 | case RootThreadBlock.TAG: 119 | return new RootThreadBlock(body); 120 | case RootMonitorUsed.TAG: 121 | return new RootMonitorUsed(body); 122 | case RootThreadObject.TAG: 123 | return new RootThreadObject(body); 124 | case ClassObject.TAG: 125 | return new ClassObject(body); 126 | case InstanceObject.TAG: 127 | return new InstanceObject(body); 128 | case ObjectArray.TAG: 129 | return new ObjectArray(body); 130 | case PrimitiveArray.TAG: 131 | return new PrimitiveArray(body); 132 | default: 133 | System.out.println(typeCode + " not recognized... @index=" + body.getIndex()); 134 | return null; 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/io/HeapProfile.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.io; 2 | 3 | import java.io.BufferedInputStream; 4 | import java.io.DataInputStream; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.nio.file.Path; 8 | 9 | 10 | public final class HeapProfile { 11 | private final Path path; 12 | private final DataInputStream input; 13 | private boolean heapDumpEnd = false; 14 | 15 | public HeapProfile(Path path, DataInputStream input) { 16 | this.path = path; 17 | this.input = input; 18 | } 19 | 20 | public boolean isAtHeapDumpEnd() throws IOException { 21 | 22 | return heapDumpEnd || input.available() == 0; 23 | } 24 | 25 | public HeapProfileHeader readHeader() throws IOException { 26 | var header = new HeapProfileHeader(); 27 | header.extract(input); 28 | 29 | return header; 30 | } 31 | 32 | private EncodedChunk readBody(DataInputStream inputStream, int bufferLength) throws IOException { 33 | byte[] buffer = new byte[bufferLength]; 34 | int bytesRead = inputStream.read(buffer); 35 | 36 | if (bytesRead < bufferLength) { 37 | this.heapDumpEnd = true; 38 | 39 | throw new IOException("bytes request exceeded bytes read"); 40 | } else { 41 | 42 | return new EncodedChunk(buffer); 43 | } 44 | } 45 | 46 | public HeapProfileRecord extract() throws IOException { 47 | byte tag = (new EncodedChunk(new byte[]{this.input.readByte()})).extractU1(); 48 | long timeStamp = (long) this.input.readInt(); 49 | int bodySize = this.input.readInt(); 50 | 51 | final var body = readBody(input, bodySize); 52 | 53 | switch (tag) { 54 | case UTF8StringSegment.TAG: 55 | 56 | return new UTF8StringSegment(body); 57 | case LoadClass.TAG: 58 | 59 | return new LoadClass(body); 60 | case UnloadClass.TAG: { 61 | System.out.println("UnloadClass"); 62 | 63 | return new UnloadClass(body); 64 | } 65 | case StackFrame.TAG: 66 | 67 | return new StackFrame(body); 68 | case StackTrace.TAG: 69 | 70 | return new StackTrace(body); 71 | case AllocSites.TAG: { 72 | System.out.println("AllocSites"); 73 | 74 | return new AllocSites(body); 75 | } 76 | case HeapSummary.TAG: { 77 | System.out.println("HeapSummary"); 78 | 79 | return new HeapSummary(body); 80 | } 81 | case StartThread.TAG: { 82 | System.out.println("StartThread"); 83 | 84 | return new StartThread(body); 85 | } 86 | case HeapDumpSegment.TAG1: 87 | case HeapDumpSegment.TAG2: 88 | 89 | return new HeapDumpSegment(body); 90 | case HeapDumpEnd.TAG: { 91 | System.out.println("HeapDumpEnd"); 92 | heapDumpEnd = true; 93 | 94 | return new HeapDumpEnd(body); 95 | } 96 | case CPUSamples.TAG: { 97 | System.out.println("CPUSamples"); 98 | 99 | return new CPUSamples(body); 100 | } 101 | case ControlSettings.TAG: { 102 | System.out.println("ControlSettings"); 103 | 104 | return new ControlSettings(body); 105 | } 106 | default: 107 | throw new IOException("Unknown record type " + tag); 108 | } 109 | 110 | } 111 | 112 | @Override 113 | public String toString() { 114 | 115 | return path.toString(); 116 | } 117 | 118 | public static HeapProfile open(Path path, InputStream inputStream) { 119 | var input = new DataInputStream(new BufferedInputStream(inputStream)); 120 | 121 | return new HeapProfile(path, input); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/io/HeapProfileHeader.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.io; 2 | 3 | /* 4 | * Copyright (c) 2018 Kirk Pepperdine. 5 | * Licensed under https://github.com/AdoptOpenJDK/jheappo/blob/master/LICENSE 6 | * Instructions: https://github.com/AdoptOpenJDK/jheappo/wiki 7 | */ 8 | 9 | import java.io.DataInputStream; 10 | import java.io.IOException; 11 | 12 | 13 | public class HeapProfileHeader { 14 | private static String SUPPORTED_VERSIONS[] = {"JAVA PROFILE 1.0.1", "JAVA PROFILE 1.0.2"}; 15 | private static int[] SUPPORTED_IDENIFIER_SIZE = {4, 8}; 16 | 17 | private String heapDumpVersion; 18 | private int sizeOfIdentifiers = 0; /* u4 is unsigned 4 bytes.. which in this case is ok to assigned to signed int */ 19 | private long millisecSinceEPOC; 20 | 21 | HeapProfileHeader() { 22 | } 23 | 24 | private String extractVersionString(DataInputStream buffer) throws IOException { 25 | char[] string = new char[1024]; 26 | int pos = 0; 27 | int value; 28 | while ((value = buffer.read()) > 0) 29 | string[pos++] = (char) value; 30 | if (value < 0) 31 | throw new IOException("Unexpected EOF"); 32 | return new String(string).trim(); 33 | } 34 | 35 | public void extract(DataInputStream buffer) throws IOException { 36 | heapDumpVersion = extractVersionString(buffer); 37 | if (!(SUPPORTED_VERSIONS[0].equals(heapDumpVersion) || (SUPPORTED_VERSIONS[1]).equals(heapDumpVersion))) { 38 | throw new IOException(heapDumpVersion + " is not supported"); 39 | } 40 | sizeOfIdentifiers = buffer.readInt(); 41 | if (sizeOfIdentifiers != SUPPORTED_IDENIFIER_SIZE[1] && sizeOfIdentifiers != SUPPORTED_IDENIFIER_SIZE[0]) { 42 | throw new IOException("Unsupported identifier size " + sizeOfIdentifiers); 43 | } 44 | millisecSinceEPOC = buffer.readLong(); 45 | } 46 | 47 | public String toString() { 48 | return heapDumpVersion + " : " + sizeOfIdentifiers + " : " + millisecSinceEPOC; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/io/HeapProfileRecord.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.io; 2 | 3 | /* 4 | * Copyright (c) 2018 Kirk Pepperdine. 5 | * Licensed under https://github.com/AdoptOpenJDK/jheappo/blob/master/LICENSE 6 | * Instructions: https://github.com/AdoptOpenJDK/jheappo/wiki 7 | */ 8 | 9 | public abstract class HeapProfileRecord { 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/io/HeapSummary.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.io; 2 | 3 | 4 | public class HeapSummary extends HeapProfileRecord { 5 | public static final int TAG = 0x07; 6 | 7 | public HeapSummary(EncodedChunk body) { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/io/LoadClass.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.io; 2 | 3 | /* 4 | * Copyright (c) 2018 Kirk Pepperdine. 5 | * Licensed under https://github.com/AdoptOpenJDK/jheappo/blob/master/LICENSE 6 | * Instructions: https://github.com/AdoptOpenJDK/jheappo/wiki 7 | */ 8 | 9 | public class LoadClass extends HeapProfileRecord { 10 | 11 | 12 | public final static int TAG = 0x02; 13 | 14 | /* 15 | u4 | class serial number (always > 0) 16 | ID | class object ID 17 | u4 | stack trace serial number 18 | ID | class name string ID 19 | */ 20 | 21 | private long classSerialNumber; 22 | private long classObjectID; 23 | private long stackTraceSerialNumber; 24 | private long classNameStringID; 25 | 26 | public LoadClass(EncodedChunk body) { 27 | classSerialNumber = body.extractU4(); 28 | classObjectID = body.extractID(); 29 | stackTraceSerialNumber = body.extractU4(); 30 | classNameStringID = body.extractID(); 31 | } 32 | 33 | public long getClassObjectID() { 34 | return classObjectID; 35 | } 36 | 37 | public long classSerialNumber() { 38 | return classSerialNumber; 39 | } 40 | 41 | public long stackTraceSerialNumber() { 42 | return stackTraceSerialNumber; 43 | } 44 | 45 | public long classNameStringID() { 46 | return classNameStringID; 47 | } 48 | 49 | public String toString() { 50 | return "Loaded -> " + classSerialNumber + ":" + classObjectID + ":" + stackTraceSerialNumber + ":" + classNameStringID; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/io/StackFrame.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.io; 2 | 3 | 4 | /* 5 | * Copyright (c) 2018 Kirk Pepperdine. 6 | * Licensed under https://github.com/AdoptOpenJDK/jheappo/blob/master/LICENSE 7 | * Instructions: https://github.com/AdoptOpenJDK/jheappo/wiki 8 | */ 9 | 10 | 11 | import org.adoptopenjdk.jheappo.io.HeapProfileRecord; 12 | 13 | /* 14 | ID | stack frame ID 15 | ID | method name string ID 16 | ID | method signature string ID 17 | ID | source file name string ID 18 | u4 | class serial number 19 | u4 | > 0 | line number 20 | | 0 | no line information available 21 | | -1 | unknown location 22 | | -2 | compiled method (Not implemented) 23 | | -3 | native method (Not implemented) 24 | */ 25 | 26 | public class StackFrame extends HeapProfileRecord { 27 | 28 | public final static int TAG = 0x04; 29 | 30 | private long stackFrameID; 31 | private long methodNameStringID; 32 | private long methodSignatureStringID; 33 | private long sourceFileNameStringID; 34 | private long classSerialNumber; 35 | 36 | public StackFrame(EncodedChunk body) { 37 | stackFrameID = body.extractID(); 38 | methodNameStringID = body.extractID(); 39 | methodSignatureStringID = body.extractID(); 40 | sourceFileNameStringID = body.extractID(); 41 | classSerialNumber = body.extractID(); 42 | } 43 | 44 | public String toString() { 45 | return "StackFrame --> " + stackFrameID + ":" + methodNameStringID + ":" + methodSignatureStringID + ":" + sourceFileNameStringID + ":" + classSerialNumber; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/io/StackTrace.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.io; 2 | 3 | /* 4 | * Copyright (c) 2018 Kirk Pepperdine. 5 | * Licensed under https://github.com/AdoptOpenJDK/jheappo/blob/master/LICENSE 6 | * Instructions: https://github.com/AdoptOpenJDK/jheappo/wiki 7 | */ 8 | 9 | 10 | import org.adoptopenjdk.jheappo.io.HeapProfileRecord; 11 | 12 | public class StackTrace extends HeapProfileRecord { 13 | 14 | public final static int TAG = 0x05; 15 | 16 | /* 17 | u4 | stack trace serial number 18 | u4 | thread serial number 19 | u4 | number of frames 20 | [ID]* | series of stack frame ID's 21 | */ 22 | 23 | private int stackTraceSerialNumber; 24 | private int threadSerialNumber; 25 | private int numberOfFrames; 26 | private long[] stackFrameIDs; 27 | 28 | public StackTrace(EncodedChunk body) { 29 | stackTraceSerialNumber = body.extractU4(); 30 | threadSerialNumber = body.extractU4(); 31 | numberOfFrames = body.extractU4(); 32 | stackFrameIDs = new long[numberOfFrames]; 33 | for (int i = 0; i < numberOfFrames; i++) { 34 | stackFrameIDs[i] = body.extractID(); 35 | } 36 | } 37 | 38 | public String toString() { 39 | 40 | return "StackTrace --> " + stackTraceSerialNumber + ":" + threadSerialNumber + ":" + numberOfFrames; 41 | } 42 | } -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/io/StartThread.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.io; 2 | 3 | 4 | public class StartThread extends HeapProfileRecord { 5 | public static final int TAG = 0x0A; 6 | 7 | public StartThread(EncodedChunk body) { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/io/UTF8StringSegment.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.io; 2 | 3 | /* 4 | * Copyright (c) 2018 Kirk Pepperdine. 5 | * Licensed under https://github.com/AdoptOpenJDK/jheappo/blob/master/LICENSE 6 | * Instructions: https://github.com/AdoptOpenJDK/jheappo/wiki 7 | */ 8 | 9 | import org.adoptopenjdk.jheappo.io.HeapProfileRecord; 10 | import org.adoptopenjdk.jheappo.objects.UTF8String; 11 | 12 | public class UTF8StringSegment extends HeapProfileRecord { 13 | 14 | /* 15 | ID | ID for this string 16 | [u1]* | UTF8 characters for string (NOT NULL terminated) 17 | */ 18 | 19 | public final static int TAG = 0x01; 20 | 21 | private final EncodedChunk body; 22 | 23 | public UTF8StringSegment(EncodedChunk body) { 24 | this.body = body; 25 | } 26 | 27 | public UTF8String toUtf8String() { 28 | // defer UTF8 parsing 29 | return new UTF8String(body.copy()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/io/UnloadClass.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.io; 2 | 3 | 4 | public class UnloadClass extends HeapProfileRecord { 5 | public static final int TAG = 0x03; 6 | 7 | /* 8 | u4 | class serial number (always > 0) 9 | */ 10 | 11 | private long classSerialNumber; 12 | 13 | public UnloadClass(EncodedChunk body) { 14 | classSerialNumber = body.extractU4(); 15 | } 16 | 17 | public String toString() { 18 | return "Unloaded -> " + classSerialNumber; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/model/ArrayValue.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.model; 2 | 3 | import org.adoptopenjdk.jheappo.objects.BasicDataTypes; 4 | 5 | 6 | public class ArrayValue extends BasicDataTypeValue { 7 | public ArrayValue() { 8 | super(BasicDataTypes.ARRAY); 9 | } 10 | 11 | @Override 12 | public String toString() { 13 | return "Array"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/model/BasicDataTypeValue.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.model; 2 | 3 | import org.adoptopenjdk.jheappo.objects.BasicDataTypes; 4 | 5 | 6 | public class BasicDataTypeValue { 7 | private Object value; 8 | private BasicDataTypes type; 9 | 10 | public BasicDataTypeValue(BasicDataTypes type) { 11 | this.type = type; 12 | } 13 | 14 | public BasicDataTypeValue(Object value, BasicDataTypes type) { 15 | this.value = value; 16 | this.type = type; 17 | } 18 | 19 | public BasicDataTypes getType() { 20 | return type; 21 | } 22 | 23 | public Object getValue() { 24 | return this.value; 25 | } 26 | 27 | public String toString() { 28 | 29 | return value + " of type " + type.toString(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/model/BooleanValue.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.model; 2 | 3 | import org.adoptopenjdk.jheappo.objects.BasicDataTypes; 4 | 5 | 6 | public class BooleanValue extends PrimitiveValue { 7 | public BooleanValue(Boolean value) { 8 | super(BasicDataTypes.BOOLEAN, value); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/model/ByteValue.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.model; 2 | 3 | import org.adoptopenjdk.jheappo.objects.BasicDataTypes; 4 | 5 | 6 | public class ByteValue extends PrimitiveValue { 7 | 8 | public ByteValue(Byte value) { 9 | super(BasicDataTypes.BYTE, value); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/model/CharValue.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.model; 2 | 3 | import org.adoptopenjdk.jheappo.objects.BasicDataTypes; 4 | 5 | 6 | public class CharValue extends PrimitiveValue { 7 | 8 | public CharValue(Character value) { 9 | super(BasicDataTypes.CHAR, value); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/model/DoubleValue.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.model; 2 | 3 | import org.adoptopenjdk.jheappo.objects.BasicDataTypes; 4 | 5 | 6 | public class DoubleValue extends PrimitiveValue { 7 | 8 | public DoubleValue( Double value) { 9 | super(BasicDataTypes.DOUBLE, value); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/model/FloatValue.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.model; 2 | 3 | import org.adoptopenjdk.jheappo.objects.BasicDataTypes; 4 | 5 | 6 | public class FloatValue extends PrimitiveValue { 7 | 8 | public FloatValue(Float value) { 9 | super(BasicDataTypes.FLOAT, value); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/model/IntValue.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.model; 2 | 3 | import org.adoptopenjdk.jheappo.objects.BasicDataTypes; 4 | 5 | 6 | public class IntValue extends PrimitiveValue { 7 | 8 | public IntValue(Integer value) { 9 | super(BasicDataTypes.INT, value); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/model/LongValue.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.model; 2 | 3 | import org.adoptopenjdk.jheappo.objects.BasicDataTypes; 4 | 5 | 6 | public class LongValue extends PrimitiveValue { 7 | 8 | public LongValue(Long value) { 9 | super(BasicDataTypes.LONG, value); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/model/ObjectValue.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.model; 2 | 3 | import org.adoptopenjdk.jheappo.objects.BasicDataTypes; 4 | 5 | 6 | public class ObjectValue extends BasicDataTypeValue { 7 | public Long objectId; 8 | 9 | public ObjectValue(Long objectId) { 10 | super(BasicDataTypes.OBJECT); 11 | this.objectId = objectId; 12 | } 13 | 14 | @Override 15 | public String toString() { 16 | 17 | return String.format("%d of type %s", objectId, getType()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/model/PrimitiveValue.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.model; 2 | 3 | import org.adoptopenjdk.jheappo.objects.BasicDataTypes; 4 | 5 | 6 | public abstract class PrimitiveValue extends BasicDataTypeValue { 7 | protected T value; 8 | 9 | public PrimitiveValue(BasicDataTypes type, T value) { 10 | super(type); 11 | this.value = value; 12 | } 13 | 14 | @Override 15 | public String toString() { 16 | 17 | return String.format("%s of type %s", value, getType()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/model/ShortValue.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.model; 2 | 3 | import org.adoptopenjdk.jheappo.objects.BasicDataTypes; 4 | 5 | 6 | public class ShortValue extends PrimitiveValue { 7 | public ShortValue(Short value) { 8 | super(BasicDataTypes.SHORT, value); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/model/UnknownValue.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.model; 2 | 3 | import org.adoptopenjdk.jheappo.objects.BasicDataTypes; 4 | 5 | 6 | public class UnknownValue extends BasicDataTypeValue { 7 | public UnknownValue() { 8 | super(BasicDataTypes.UNKNOWN); 9 | } 10 | 11 | @Override 12 | public String toString() { 13 | 14 | return "Unknown"; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/objects/BasicDataTypes.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.objects; 2 | 3 | /* 4 | * Copyright (c) 2018 Kirk Pepperdine. 5 | * Licensed under https://github.com/AdoptOpenJDK/jheappo/blob/master/LICENSE 6 | * Instructions: https://github.com/AdoptOpenJDK/jheappo/wiki 7 | */ 8 | 9 | import java.util.stream.Stream; 10 | 11 | public enum BasicDataTypes { 12 | 13 | OBJECT(2, "Object", 'L', 8), 14 | BOOLEAN(4, "boolean", 'V', 1), 15 | CHAR(5, "char", 'C', 2), 16 | FLOAT(6, "float", 'F', 4), 17 | DOUBLE(7, "double", 'F', 8), 18 | BYTE(8, "byte", 'B', 1), 19 | SHORT(9, "short", 'S', 2), 20 | INT(10, "int", 'I', 4), 21 | LONG(11, "long", 'L', 8), 22 | // TODO Not found in https://hg.openjdk.java.net/jdk/jdk/file/9a73a4e4011f/src/hotspot/share/services/heapDumper.cpp 23 | ARRAY(12, "", '[', 0), 24 | UNKNOWN(-1, "", ' ', -1); 25 | 26 | 27 | private final int ordinalValue; 28 | private final String label; 29 | private final char mnemonic; 30 | private final int size; 31 | 32 | BasicDataTypes(int ordinalValue, String label, char mnemonic, int size) { 33 | this.ordinalValue = ordinalValue; 34 | this.label = label; 35 | this.mnemonic = mnemonic; 36 | this.size = size; 37 | } 38 | 39 | public int getOrdinalValue() { 40 | return ordinalValue; 41 | } 42 | 43 | public String getLabel() { 44 | return label; 45 | } 46 | 47 | public char getMnemonic() { 48 | return mnemonic; 49 | } 50 | 51 | public int getSize() { 52 | return size; 53 | } 54 | 55 | public static BasicDataTypes fromInt(int typeIndex) { 56 | 57 | return Stream.of(values()) 58 | .filter(it -> it.ordinalValue == typeIndex) 59 | .findFirst() 60 | .orElse(null); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/objects/ClassObject.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.objects; 2 | 3 | /* 4 | * Copyright (c) 2018 Kirk Pepperdine. 5 | * Licensed under https://github.com/AdoptOpenJDK/jheappo/blob/master/LICENSE 6 | * Instructions: https://github.com/AdoptOpenJDK/jheappo/wiki 7 | */ 8 | 9 | import org.adoptopenjdk.jheappo.io.EncodedChunk; 10 | import org.adoptopenjdk.jheappo.model.BasicDataTypeValue; 11 | 12 | /* 13 | 0x20 | ID | class object ID 14 | | u4 | stack trace serial number 15 | | ID | super class object ID 16 | | ID | class loader object ID 17 | | ID | signers object ID 18 | | ID | protection domain object ID 19 | | ID | reserved 20 | | ID | reserved 21 | | u4 | instance size (in bytes) 22 | | u2 | size of constant pool and number of records that follow: 23 | | u2 | constant pool index 24 | | u1 | type of entry: (See Basic Type) 25 | | value | value of entry (u1, u2, u4, or u8 based on type of entry) 26 | | u2 | Number of static fields: 27 | | ID | static field name string ID 28 | | u1 | type of field: (See Basic Type) 29 | | value | value of entry (u1, u2, u4, or u8 based on type of field) 30 | | u2 | Number of instance fields (not including super class's) 31 | | ID | field name string ID 32 | | u1 | type of field: (See Basic Type) 33 | 34 | Basic Types 35 | 2 | object 36 | 4 | boolean 37 | 5 | char 38 | 6 | float 39 | 7 | double 40 | 8 | byte 41 | 9 | short 42 | 10 | int 43 | 11 | long 44 | */ 45 | 46 | public class ClassObject extends HeapObject { 47 | 48 | public static final int TAG = 0x20; 49 | 50 | private long superClassObjectID; 51 | private long classLoaderObjectID; 52 | private int stackTraceSerialNumber; 53 | private long signersObjectID; 54 | private long protectionDomainObjectID; 55 | private long[] reserved = new long[2]; 56 | private int instanceSizeInBytes; 57 | 58 | private long[] staticFieldNameIndicies; 59 | private BasicDataTypeValue[] staticValues; 60 | 61 | private long[] fieldNamesIndicies; 62 | private int[] fieldTypes; 63 | 64 | public ClassObject(EncodedChunk buffer) { 65 | super(buffer); //classObjectID; 66 | extractPoolData(buffer); 67 | } 68 | 69 | private void extractPoolData(EncodedChunk buffer) { 70 | stackTraceSerialNumber = buffer.extractU4(); 71 | superClassObjectID = buffer.extractID(); 72 | classLoaderObjectID = buffer.extractID(); 73 | signersObjectID = buffer.extractID(); 74 | protectionDomainObjectID = buffer.extractID(); 75 | reserved[0] = buffer.extractID(); 76 | reserved[1] = buffer.extractID(); 77 | instanceSizeInBytes = buffer.extractU4(); 78 | extractConstantPool(buffer); 79 | extractStaticFields(buffer); 80 | extractInstanceFields(buffer); 81 | } 82 | 83 | /* 84 | | u2 | size of constant pool and number of records that follow: 85 | | u2 | constant pool index 86 | | u1 | type of entry: (See Basic Type) 87 | | value | value of entry (u1, u2, u4, or u8 based on type of entry) 88 | */ 89 | private void extractConstantPool(EncodedChunk buffer) { 90 | int numberOfRecords = buffer.extractU2(); 91 | for (int i = 0; i < numberOfRecords; i++) { 92 | int constantPoolIndex = buffer.extractU2(); 93 | BasicDataTypeValue value = buffer.extractBasicType(buffer.extractU1()); 94 | System.out.println("Constant Pool: " + constantPoolIndex + ":" + value.toString()); 95 | } 96 | } 97 | 98 | /* 99 | | u2 | Number of static fields: 100 | | ID | static field name string ID 101 | | u1 | type of field: (See Basic Type) 102 | | value | value of entry (u1, u2, u4, or u8 based on type of field) 103 | */ 104 | private void extractStaticFields(EncodedChunk buffer) { 105 | int numberOfRecords = buffer.extractU2(); 106 | staticFieldNameIndicies = new long[numberOfRecords]; 107 | staticValues = new BasicDataTypeValue[numberOfRecords]; 108 | 109 | for (int i = 0; i < numberOfRecords; i++) { 110 | staticFieldNameIndicies[i] = buffer.extractID(); 111 | staticValues[i] = buffer.extractBasicType(buffer.extractU1()); 112 | } 113 | } 114 | 115 | /* 116 | | u2 | Number of instance fields (not including super class's) 117 | | ID | field name string ID 118 | | u1 | type of field: (See Basic Type) 119 | */ 120 | private void extractInstanceFields(EncodedChunk buffer) { 121 | int numberOfInstanceFields = buffer.extractU2(); 122 | if (numberOfInstanceFields > -1) { 123 | fieldNamesIndicies = new long[numberOfInstanceFields]; 124 | fieldTypes = new int[numberOfInstanceFields]; 125 | } 126 | for (int i = 0; i < numberOfInstanceFields; i++) { 127 | fieldNamesIndicies[i] = buffer.extractID(); 128 | if (fieldNamesIndicies[i] < 1) 129 | System.out.println("field name invalid id: " + fieldNamesIndicies[i]); 130 | fieldTypes[i] = buffer.extractU1(); 131 | } 132 | } 133 | 134 | public String toString() { 135 | var fields = ", Fields --> "; 136 | 137 | for (long fieldNamesIndicy : fieldNamesIndicies) { 138 | fields += ", " + fieldNamesIndicy; 139 | } 140 | 141 | return "Class Object -->" + getId() + 142 | ", stackTraceSerialNumber -->" + stackTraceSerialNumber + 143 | ", superClassObjectID -->" + superClassObjectID + 144 | ", classLoaderObjectID -->" + classLoaderObjectID + 145 | ", signersObjectID -->" + signersObjectID + 146 | ", protectionDomainObjectID -->" + protectionDomainObjectID + 147 | fields; 148 | } 149 | 150 | public int[] fieldTypes() { 151 | return fieldTypes; 152 | } 153 | 154 | public long[] fieldNameIndicies() { 155 | return fieldNamesIndicies; 156 | } 157 | 158 | public long superClassObjectID() { 159 | return superClassObjectID; 160 | } 161 | 162 | public int instanceSizeInBytes() { 163 | return instanceSizeInBytes; 164 | } 165 | 166 | public long[] staticFieldNameIndicies() { 167 | return staticFieldNameIndicies; 168 | } 169 | 170 | public BasicDataTypeValue[] staticValues() { 171 | return staticValues; 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/objects/HeapObject.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.objects; 2 | 3 | 4 | import org.adoptopenjdk.jheappo.io.EncodedChunk; 5 | 6 | public abstract class HeapObject { 7 | 8 | private long id; 9 | 10 | public HeapObject() { 11 | this.id = -1; 12 | } 13 | 14 | public HeapObject(EncodedChunk buffer) { 15 | this.id = buffer.extractID(); 16 | } 17 | 18 | public long getId() { 19 | 20 | return id; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/objects/InstanceObject.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.objects; 2 | 3 | 4 | /* 5 | * Copyright (c) 2018 Kirk Pepperdine. 6 | * Licensed under https://github.com/AdoptOpenJDK/jheappo/blob/master/LICENSE 7 | * Instructions: https://github.com/AdoptOpenJDK/jheappo/wiki 8 | */ 9 | 10 | import org.adoptopenjdk.jheappo.io.EncodedChunk; 11 | import org.adoptopenjdk.jheappo.model.BasicDataTypeValue; 12 | 13 | /* 14 | ID object ID 15 | u4 stack trace serial number 16 | ID class object ID 17 | u4 number of bytes that follow 18 | [value]* instance field values (this class, followed by super class, etc) 19 | */ 20 | public class InstanceObject extends HeapObject { 21 | 22 | public final static int TAG = 0x21; 23 | 24 | private int stackTraceSerialNumber; 25 | private long classObjectID; 26 | private BasicDataTypeValue[] instanceFieldValues = new BasicDataTypeValue[0]; 27 | private EncodedChunk buffer; 28 | 29 | public InstanceObject(EncodedChunk buffer) { 30 | super(buffer); 31 | this.buffer = buffer; 32 | stackTraceSerialNumber = buffer.extractU4(); 33 | classObjectID = buffer.extractID(); 34 | int bufferLength = buffer.extractU4(); 35 | this.buffer = new EncodedChunk(buffer.read(bufferLength)); 36 | } 37 | 38 | public void inflate(ClassObject classObject) { 39 | if (buffer == null) return; 40 | if (!buffer.endOfBuffer()) { 41 | int[] fieldTypes = classObject.fieldTypes(); 42 | instanceFieldValues = new BasicDataTypeValue[fieldTypes.length]; 43 | for (int i = 0; i < fieldTypes.length; i++) { 44 | instanceFieldValues[i] = buffer.extractBasicType(fieldTypes[i]); 45 | } 46 | } 47 | buffer = null; 48 | } 49 | 50 | public String toString() { 51 | String prefix = "InstanceObject->" + classObjectID; 52 | if (instanceFieldValues.length > 0) 53 | prefix += " fields --> "; 54 | for (int i = 0; i < instanceFieldValues.length; i++) { 55 | prefix += instanceFieldValues[i].toString() + ", "; 56 | } 57 | return prefix; 58 | } 59 | 60 | public int stackTraceSerialNumber() { 61 | return stackTraceSerialNumber; 62 | } 63 | 64 | public long classObjectID() { 65 | return classObjectID; 66 | } 67 | 68 | public BasicDataTypeValue[] instanceFieldValues() { 69 | return instanceFieldValues; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/objects/ObjectArray.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.objects; 2 | /* 3 | * Copyright (c) 2018 Kirk Pepperdine. 4 | * Licensed under https://github.com/AdoptOpenJDK/jheappo/blob/master/LICENSE 5 | * Instructions: https://github.com/AdoptOpenJDK/jheappo/wiki 6 | */ 7 | 8 | import org.adoptopenjdk.jheappo.io.EncodedChunk; 9 | import org.adoptopenjdk.jheappo.model.BasicDataTypeValue; 10 | 11 | /* 12 | ID array object ID 13 | u4 stack trace serial number 14 | u4 number of elements 15 | ID array class object ID 16 | [ID]* elements 17 | */ 18 | public class ObjectArray extends HeapObject { 19 | 20 | public static final int TAG = 0x22; 21 | 22 | private int stackTraceSerialNumber; 23 | private int size; 24 | private long elementsObjectID; 25 | private BasicDataTypeValue[] elements; 26 | 27 | public ObjectArray(EncodedChunk buffer) { 28 | super(buffer); 29 | stackTraceSerialNumber = buffer.extractInt(); 30 | size = buffer.extractInt(); 31 | elementsObjectID = buffer.extractID(); 32 | elements = new BasicDataTypeValue[size]; 33 | for ( int i = 0; i < size; i++) { 34 | elements[i] = buffer.extractBasicType(BasicDataTypes.OBJECT); 35 | } 36 | } 37 | 38 | public int getStackTraceSerialNumber() { 39 | return stackTraceSerialNumber; 40 | } 41 | 42 | public int getSize() { 43 | return size; 44 | } 45 | 46 | public long getElementsObjectID() { 47 | return elementsObjectID; 48 | } 49 | 50 | public long getValueObjectIDAt(int index) { 51 | 52 | return ((Long)elements[index].getValue()).longValue(); 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/objects/PrimitiveArray.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.objects; 2 | 3 | /* 4 | * Copyright (c) 2018 Kirk Pepperdine. 5 | * Licensed under https://github.com/AdoptOpenJDK/jheappo/blob/master/LICENSE 6 | * Instructions: https://github.com/AdoptOpenJDK/jheappo/wiki 7 | */ 8 | 9 | import org.adoptopenjdk.jheappo.io.EncodedChunk; 10 | import org.adoptopenjdk.jheappo.model.BasicDataTypeValue; 11 | 12 | 13 | public class PrimitiveArray extends HeapObject { 14 | 15 | public static final int TAG = 0x23; 16 | 17 | private int stackTraceSerialNumber; 18 | private int size; 19 | private byte elementType; 20 | private char signature = ' '; 21 | private BasicDataTypeValue[] elements; 22 | 23 | public PrimitiveArray(EncodedChunk buffer) { 24 | super(buffer); 25 | stackTraceSerialNumber = buffer.extractInt(); 26 | size = buffer.extractInt(); 27 | elementType = buffer.extractByte(); 28 | readArray(buffer, elementType, size); 29 | } 30 | 31 | private void readArray(EncodedChunk buffer, byte elementType, int size) { 32 | var dataType = BasicDataTypes.fromInt(elementType); 33 | signature = BasicDataTypes.fromInt(elementType).getMnemonic(); 34 | 35 | if (dataType.equals(BasicDataTypes.UNKNOWN)) { 36 | throw new IllegalArgumentException("Unknown data type : " + elementType); 37 | } 38 | 39 | elements = new BasicDataTypeValue[size]; 40 | 41 | for (int i = 0; i < size; i++) { 42 | elements[i] = buffer.extractBasicType(dataType); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/objects/RootJNIGlobal.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.objects; 2 | 3 | 4 | /* 5 | * Copyright (c) 2018 Kirk Pepperdine. 6 | * Licensed under https://github.com/AdoptOpenJDK/jheappo/blob/master/LICENSE 7 | * Instructions: https://github.com/AdoptOpenJDK/jheappo/wiki 8 | */ 9 | 10 | 11 | import org.adoptopenjdk.jheappo.io.EncodedChunk; 12 | 13 | /* 14 | 0x01 | ID | object ID 15 | | ID | JNI global ref ID 16 | */ 17 | public class RootJNIGlobal extends HeapObject { 18 | 19 | public static final int TAG = 0x01; 20 | 21 | private long jniGlobalRefID; 22 | 23 | public RootJNIGlobal(EncodedChunk buffer) { 24 | super(buffer); 25 | jniGlobalRefID = buffer.extractID(); 26 | } 27 | 28 | public long getJNIGlobalRefID() { 29 | 30 | return jniGlobalRefID; 31 | } 32 | 33 | public String toString() { 34 | 35 | return "RootJNIGlobal : " + getId() + ":" + jniGlobalRefID; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/objects/RootJNILocal.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.objects; 2 | 3 | /* 4 | * Copyright (c) 2018 Kirk Pepperdine. 5 | * Licensed under https://github.com/AdoptOpenJDK/jheappo/blob/master/LICENSE 6 | * Instructions: https://github.com/AdoptOpenJDK/jheappo/wiki 7 | */ 8 | 9 | 10 | import org.adoptopenjdk.jheappo.io.EncodedChunk; 11 | 12 | public class RootJNILocal extends HeapObject { 13 | 14 | public final static int TAG = 0x02; 15 | 16 | private int threadSerialNumber; 17 | private int frameNumberInStackTrace; // -1 if empty 18 | 19 | public RootJNILocal(EncodedChunk buffer) { 20 | super(buffer); 21 | threadSerialNumber = buffer.extractU4(); 22 | frameNumberInStackTrace = buffer.extractU4(); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/objects/RootJavaFrame.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.objects; 2 | 3 | /* 4 | * Copyright (c) 2018 Kirk Pepperdine. 5 | * Licensed under https://github.com/AdoptOpenJDK/jheappo/blob/master/LICENSE 6 | * Instructions: https://github.com/AdoptOpenJDK/jheappo/wiki 7 | */ 8 | 9 | 10 | import org.adoptopenjdk.jheappo.io.EncodedChunk; 11 | 12 | public class RootJavaFrame extends HeapObject { 13 | 14 | public final static int TAG = 0x03; 15 | 16 | private int threadSerialNumber; 17 | private int frameNumberInStackTrace; // -1 if empty 18 | 19 | public RootJavaFrame(EncodedChunk buffer) { 20 | super(buffer); 21 | threadSerialNumber = buffer.extractU4(); 22 | frameNumberInStackTrace = buffer.extractU4(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/objects/RootMonitorUsed.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.objects; 2 | 3 | /* 4 | * Copyright (c) 2018 Kirk Pepperdine. 5 | * Licensed under https://github.com/AdoptOpenJDK/jheappo/blob/master/LICENSE 6 | * Instructions: https://github.com/AdoptOpenJDK/jheappo/wiki 7 | */ 8 | 9 | 10 | import org.adoptopenjdk.jheappo.io.EncodedChunk; 11 | 12 | public class RootMonitorUsed extends HeapObject { 13 | 14 | public final static int TAG = 0x07; 15 | 16 | public RootMonitorUsed(EncodedChunk buffer) { 17 | super(buffer); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/objects/RootNativeStack.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.objects; 2 | 3 | /* 4 | * Copyright (c) 2018 Kirk Pepperdine. 5 | * Licensed under https://github.com/AdoptOpenJDK/jheappo/blob/master/LICENSE 6 | * Instructions: https://github.com/AdoptOpenJDK/jheappo/wiki 7 | */ 8 | 9 | 10 | import org.adoptopenjdk.jheappo.io.EncodedChunk; 11 | 12 | public class RootNativeStack extends HeapObject { 13 | 14 | public final static int TAG = 0x04; 15 | 16 | private int threadSerialNumber; 17 | 18 | public RootNativeStack(EncodedChunk buffer) { 19 | super(buffer); 20 | threadSerialNumber = buffer.extractU4(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/objects/RootStickyClass.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.objects; 2 | 3 | /* 4 | * Copyright (c) 2018 Kirk Pepperdine. 5 | * Licensed under https://github.com/AdoptOpenJDK/jheappo/blob/master/LICENSE 6 | * Instructions: https://github.com/AdoptOpenJDK/jheappo/wiki 7 | */ 8 | 9 | 10 | import org.adoptopenjdk.jheappo.io.EncodedChunk; 11 | 12 | public class RootStickyClass extends HeapObject { 13 | 14 | public static final int TAG = 0x05; 15 | 16 | public RootStickyClass(EncodedChunk buffer) { 17 | super(buffer); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/objects/RootThreadBlock.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.objects; 2 | 3 | /* 4 | * Copyright (c) 2018 Kirk Pepperdine. 5 | * Licensed under https://github.com/AdoptOpenJDK/jheappo/blob/master/LICENSE 6 | * Instructions: https://github.com/AdoptOpenJDK/jheappo/wiki 7 | */ 8 | 9 | 10 | import org.adoptopenjdk.jheappo.io.EncodedChunk; 11 | 12 | public class RootThreadBlock extends HeapObject { 13 | 14 | public final static int TAG = 0x06; 15 | 16 | private int threadSerialNumber; 17 | 18 | public RootThreadBlock(EncodedChunk buffer) { 19 | super( buffer); 20 | threadSerialNumber = buffer.extractU4(); 21 | } 22 | 23 | public String toString() { 24 | 25 | return "Root Sticky Class : " + getId() + " : " + threadSerialNumber; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/objects/RootThreadObject.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.objects; 2 | 3 | /* 4 | * Copyright (c) 2018 Kirk Pepperdine. 5 | * Licensed under https://github.com/AdoptOpenJDK/jheappo/blob/master/LICENSE 6 | * Instructions: https://github.com/AdoptOpenJDK/jheappo/wiki 7 | */ 8 | 9 | /* 10 | 0x08 | ID | thread object ID 11 | | u4 | thread serial number 12 | | u4 | stack trace serial number 13 | */ 14 | 15 | 16 | import org.adoptopenjdk.jheappo.io.EncodedChunk; 17 | 18 | public class RootThreadObject extends HeapObject { 19 | 20 | public static final int TAG = 0x08; 21 | 22 | private int serialNumber; 23 | private int traceSerialNumber; 24 | 25 | public RootThreadObject(EncodedChunk buffer) { 26 | super(buffer); 27 | serialNumber = buffer.extractU4(); 28 | traceSerialNumber = buffer.extractU4(); 29 | } 30 | 31 | public int getSerialNumber() { return this.serialNumber; } 32 | public int getTraceSerialNumber() { return this.traceSerialNumber; } 33 | 34 | @Override 35 | public String toString() { 36 | return "Root Thread Object : " + serialNumber + " : " + traceSerialNumber; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/objects/RootUnknown.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.objects; 2 | 3 | /* 4 | * Copyright (c) 2018 Kirk Pepperdine. 5 | * Licensed under https://github.com/AdoptOpenJDK/jheappo/blob/master/LICENSE 6 | * Instructions: https://github.com/AdoptOpenJDK/jheappo/wiki 7 | */ 8 | 9 | 10 | import org.adoptopenjdk.jheappo.io.EncodedChunk; 11 | 12 | public class RootUnknown extends HeapObject { 13 | 14 | public final static int TAG = 0xFF; 15 | 16 | public RootUnknown(EncodedChunk buffer) { 17 | super(buffer); 18 | } 19 | 20 | @Override 21 | public String toString() { 22 | return "Root Unknown : " + getId(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /core/src/main/java/org/adoptopenjdk/jheappo/objects/UTF8String.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.objects; 2 | 3 | /* 4 | * Copyright (c) 2018 Kirk Pepperdine. 5 | * Licensed under https://github.com/AdoptOpenJDK/jheappo/blob/master/LICENSE 6 | * Instructions: https://github.com/AdoptOpenJDK/jheappo/wiki 7 | */ 8 | 9 | 10 | import org.adoptopenjdk.jheappo.io.EncodedChunk; 11 | 12 | public class UTF8String extends HeapObject { 13 | 14 | private String string; 15 | 16 | public UTF8String(EncodedChunk buffer) { 17 | super(buffer); 18 | this.string = new String(buffer.readRemaining()); 19 | } 20 | 21 | public String getString() { 22 | 23 | return string; 24 | } 25 | 26 | public String toString() { 27 | 28 | return getId() + " : " + string; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /core/src/test/java/org/adoptopenjdk/jheappo/io/HeapDumpSectionTest.kt: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.io 2 | 3 | import org.junit.Test 4 | 5 | import junit.framework.TestCase.assertEquals 6 | import junit.framework.TestCase.assertTrue 7 | 8 | class HeapDumpSectionTest { 9 | 10 | //@Test 11 | fun testBufferReading() { 12 | val one = byteArrayOf(0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 10, 10, 10, 10) 13 | val buffer = EncodedChunk(one) 14 | assertEquals("", 0U, buffer.extractU1().toUInt()) 15 | assertEquals("", 0U, buffer.extractU2().toUInt()) 16 | assertEquals("", 16777216U, buffer.extractU4()) 17 | assertEquals("", 72340172989663754U, buffer.extractU8()) 18 | assertTrue(buffer.endOfBuffer()) 19 | } 20 | 21 | //@Test 22 | fun testBufferReadingUnsignedWithHighBitSet() { 23 | // 8 bytes with the high bit set 24 | val bytes = (1..8).map { Byte.MIN_VALUE }.toByteArray() 25 | assertEquals(128U, EncodedChunk(bytes).extractU1().toUInt()) 26 | // bits 7 and 15 27 | assertEquals((0..1).map { 1U shl (it * 8 + 7)}.sum(), EncodedChunk(bytes).extractU2().toUInt()) 28 | // bits 7, 15, 23, 31 29 | assertEquals((0..3).map { 1U shl (it * 8 + 7)}.sum(), EncodedChunk(bytes).extractU4()) 30 | // bits 7, 15, ... 63 31 | assertEquals((0..7).map { 1UL shl (it * 8 + 7)}.sum(), EncodedChunk(bytes).extractU8()) 32 | } 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /data/HeapDumpSegment.hprof: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdoptOpenJDK/jheappo/6af917c23830c989e7e11efbe2e534ebd2dbe8be/data/HeapDumpSegment.hprof -------------------------------------------------------------------------------- /data/mastermind.hprof: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdoptOpenJDK/jheappo/6af917c23830c989e7e11efbe2e534ebd2dbe8be/data/mastermind.hprof -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | jheappo 6 | jheappo-parent 7 | 1.0.0-SNAPSHOT 8 | pom 9 | 10 | jheappo 11 | JHeappo Heap Parser 12 | 13 | 14 | 15 | 3.6.1 16 | 17 | 18 | 19 | core 20 | tools 21 | 22 | 23 | 24 | 0.8.4 25 | 3.5.6 26 | UTF-8 27 | 3.0.0-M3 28 | 1.3.31 29 | 5.5.0-M1 30 | 31 | 32 | 33 | 34 | org.jetbrains.kotlin 35 | kotlin-stdlib-jdk8 36 | ${kotlin.version} 37 | 38 | 39 | 40 | org.junit.vintage 41 | junit-vintage-engine 42 | ${junit.version} 43 | test 44 | 45 | 46 | junit 47 | junit 48 | 4.13.1 49 | test 50 | 51 | 52 | org.junit.jupiter 53 | junit-jupiter-api 54 | ${junit.version} 55 | test 56 | 57 | 58 | org.junit.jupiter 59 | junit-jupiter-engine 60 | ${junit.version} 61 | test 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | com.github.spotbugs 70 | spotbugs-maven-plugin 71 | 3.1.11 72 | 73 | 74 | com.ning.maven.plugins 75 | maven-dependency-versions-check-plugin 76 | 2.0.4 77 | 78 | true 79 | 80 | 81 | 82 | verify 83 | 84 | check 85 | 86 | 87 | 88 | 89 | 90 | org.apache.maven.plugins 91 | maven-antrun-plugin 92 | 1.8 93 | 94 | 95 | org.apache.maven.plugins 96 | maven-assembly-plugin 97 | 3.1.1 98 | 99 | 100 | org.apache.maven.plugins 101 | maven-clean-plugin 102 | 3.1.0 103 | 104 | 105 | org.apache.maven.plugins 106 | maven-compiler-plugin 107 | 3.8.1 108 | 109 | 11 110 | UTF-8 111 | 112 | 113 | 114 | 115 | default-compile 116 | none 117 | 118 | 119 | 120 | default-testCompile 121 | none 122 | 123 | 124 | java-compile 125 | compile 126 | compile 127 | 128 | 129 | java-test-compile 130 | test-compile 131 | testCompile 132 | 133 | 134 | 135 | 136 | org.apache.maven.plugins 137 | maven-dependency-plugin 138 | 3.1.1 139 | 140 | 141 | org.apache.maven.shared 142 | maven-dependency-analyzer 143 | 1.11.1 144 | 145 | 146 | 147 | 148 | org.apache.maven.plugins 149 | maven-help-plugin 150 | 3.2.0 151 | 152 | 153 | org.apache.maven.plugins 154 | maven-jdeprscan-plugin 155 | 3.0.0-alpha-1 156 | 157 | 158 | org.apache.maven.plugins 159 | maven-jdeps-plugin 160 | 3.1.1 161 | 162 | 163 | 164 | jdkinternals 165 | test-jdkinternals 166 | 167 | 168 | 169 | 170 | 171 | org.apache.maven.plugins 172 | maven-deploy-plugin 173 | 3.0.0-M1 174 | 175 | 176 | org.apache.maven.plugins 177 | maven-enforcer-plugin 178 | 3.0.0-M2 179 | 180 | 181 | enforce-versions 182 | 183 | enforce 184 | 185 | 186 | 187 | 188 | 3.6.1 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | org.apache.maven.plugins 197 | maven-failsafe-plugin 198 | ${surefire.version} 199 | 200 | 201 | org.apache.maven.shared 202 | maven-filtering 203 | 3.1.1 204 | 205 | 206 | org.apache.maven.plugins 207 | maven-install-plugin 208 | 3.0.0-M1 209 | 210 | 211 | org.apache.maven.plugins 212 | maven-javadoc-plugin 213 | 3.1.0 214 | 215 | 216 | org.apache.maven.plugins 217 | maven-jar-plugin 218 | 3.1.2 219 | 220 | 221 | org.apache.maven.plugins 222 | maven-jxr-plugin 223 | 3.0.0 224 | 225 | 226 | org.apache.maven.plugins 227 | maven-pmd-plugin 228 | 3.12.0 229 | 230 | 11 231 | 232 | 233 | 234 | org.apache.maven.plugins 235 | maven-project-info-reports-plugin 236 | 3.0.0 237 | 238 | 239 | org.apache.maven.plugins 240 | maven-release-plugin 241 | 2.5.3 242 | 243 | 244 | org.apache.maven.plugins 245 | maven-resources-plugin 246 | 3.1.0 247 | 248 | 249 | org.apache.maven.plugins 250 | maven-scm-plugin 251 | 1.11.2 252 | 253 | 254 | org.apache.maven.plugins 255 | maven-surefire-plugin 256 | ${surefire.version} 257 | 258 | 259 | org.apache.maven.plugins 260 | maven-site-plugin 261 | 3.7.1 262 | 263 | 264 | org.apache.maven.plugins 265 | maven-source-plugin 266 | 3.1.0 267 | 268 | 269 | org.apache.rat 270 | apache-rat-plugin 271 | 0.13 272 | 273 | 274 | verify 275 | 276 | check 277 | 278 | 279 | 280 | 281 | 282 | org.codehaus.mojo 283 | exec-maven-plugin 284 | 1.6.0 285 | 286 | 287 | org.codehaus.mojo 288 | license-maven-plugin 289 | 1.20 290 | 291 | 292 | download-licenses 293 | 294 | download-licenses 295 | 296 | 297 | 298 | 299 | 300 | org.codehaus.mojo 301 | tidy-maven-plugin 302 | 1.1.0 303 | 304 | 305 | validate 306 | validate 307 | 308 | check 309 | 310 | 311 | 312 | 313 | 314 | org.codehaus.mojo 315 | versions-maven-plugin 316 | 2.7 317 | 318 | 319 | org.jacoco 320 | jacoco-maven-plugin 321 | ${jacoco.version} 322 | 323 | 324 | default-prepare-agent 325 | 326 | prepare-agent 327 | 328 | 329 | 330 | default-report 331 | prepare-package 332 | 333 | report 334 | 335 | 336 | 337 | 338 | 339 | 357 | 358 | org.pitest 359 | pitest-maven 360 | 1.4.8 361 | 362 | 363 | 364 | 365 | 366 | com.ning.maven.plugins 367 | maven-dependency-versions-check-plugin 368 | 369 | 370 | org.codehaus.mojo 371 | license-maven-plugin 372 | 373 | 374 | org.codehaus.mojo 375 | tidy-maven-plugin 376 | 377 | 378 | org.jacoco 379 | jacoco-maven-plugin 380 | 381 | 387 | 388 | org.jetbrains.kotlin 389 | kotlin-maven-plugin 390 | ${kotlin.version} 391 | 392 | 393 | compile 394 | compile 395 | 396 | -Xuse-experimental=kotlin.ExperimentalUnsignedTypes 397 | 398 | ${project.basedir}/src/main/kotlin 399 | ${project.basedir}/src/main/java 400 | 401 | 402 | 403 | 404 | test-compile 405 | test-compile 406 | 407 | -Xuse-experimental=kotlin.ExperimentalUnsignedTypes 408 | 409 | ${project.basedir}/src/test/kotlin 410 | ${project.basedir}/src/test/java 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | com.github.spotbugs 423 | spotbugs-maven-plugin 424 | 3.1.11 425 | 426 | 427 | 428 | org.mutabilitydetector 429 | MutabilityDetector4FindBugs 430 | 0.9.1 431 | 432 | 433 | 434 | 435 | 436 | org.apache.maven.plugins 437 | maven-changes-plugin 438 | 2.12.1 439 | 440 | https 441 | 443 442 | false 443 | false 444 | 445 | 446 | 447 | 448 | changes-report 449 | 450 | 451 | 452 | 453 | 454 | org.apache.maven.plugins 455 | maven-dependency-plugin 456 | 3.1.1 457 | 458 | 459 | org.apache.maven.plugins 460 | maven-jxr-plugin 461 | 3.0.0 462 | 463 | 464 | org.apache.maven.plugins 465 | maven-pmd-plugin 466 | 3.12.0 467 | 468 | 11 469 | 470 | 471 | 472 | org.apache.maven.plugins 473 | maven-project-info-reports-plugin 474 | 3.0.0 475 | 476 | 477 | org.apache.rat 478 | apache-rat-plugin 479 | 0.13 480 | 481 | 482 | org.codehaus.mojo 483 | license-maven-plugin 484 | 1.20 485 | 486 | 487 | org.codehaus.mojo 488 | versions-maven-plugin 489 | 2.7 490 | 491 | 492 | 493 | dependency-updates-report 494 | plugin-updates-report 495 | property-updates-report 496 | 497 | 498 | 499 | 500 | 501 | org.jacoco 502 | jacoco-maven-plugin 503 | ${jacoco.version} 504 | 505 | 523 | 524 | 525 | 526 | -------------------------------------------------------------------------------- /tools/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | jheappo 7 | jheappo-parent 8 | 1.0.0-SNAPSHOT 9 | 10 | 11 | jheappo-tools 12 | 13 | 14 | 15 | jheappo 16 | jheappo-core 17 | 1.0.0-SNAPSHOT 18 | 19 | 20 | org.neo4j 21 | neo4j 22 | ${neo4j.version} 23 | 24 | 25 | 26 | org.neo4j 27 | neo4j-kernel 28 | ${neo4j.version} 29 | test-jar 30 | test 31 | 32 | 33 | org.neo4j 34 | neo4j-io 35 | ${neo4j.version} 36 | test-jar 37 | test 38 | 39 | 40 | 41 | 42 | 43 | 44 | org.apache.maven.plugins 45 | maven-shade-plugin 46 | 3.2.1 47 | 48 | 49 | package 50 | 51 | shade 52 | 53 | 54 | 55 | 56 | *:* 57 | 58 | META-INF/*.SF 59 | META-INF/*.DSA 60 | META-INF/*.RSA 61 | 62 | 63 | 64 | 65 | 67 | 68 | org.adoptopenjdk.jheappo.Heappo 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /tools/src/main/java/org/adoptopenjdk/jheappo/Heappo.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo; 2 | 3 | /** 4 | * /* 5 | * Copyright (c) 2018 Kirk Pepperdine. 6 | * Licensed under https://github.com/AdoptOpenJDK/jheappo/blob/master/LICENSE 7 | * Instructions: https://github.com/AdoptOpenJDK/jheappo/wiki 8 | */ 9 | 10 | import org.adoptopenjdk.jheappo.io.HeapProfile; 11 | import org.adoptopenjdk.jheappo.model.HeapGraph; 12 | import org.adoptopenjdk.jheappo.model.JavaHeap; 13 | 14 | import java.io.DataInputStream; 15 | import java.io.File; 16 | import java.io.FileInputStream; 17 | import java.io.IOException; 18 | import java.nio.file.Path; 19 | import java.nio.file.Paths; 20 | 21 | public class Heappo { 22 | 23 | public static void main(String[] args) throws IOException { 24 | Path path = new File(args[0]).toPath(); 25 | HeapProfile heapDump = new HeapProfile(path, new DataInputStream(new FileInputStream(path.toFile()))); 26 | if (args.length > 1 && args[1].equalsIgnoreCase("graph")) { 27 | HeapGraph graph = new HeapGraph(new File("graph.db")); 28 | graph.populateFrom(heapDump); 29 | } else { 30 | JavaHeap heap = new JavaHeap(Paths.get(".")); 31 | heap.populateFrom(heapDump); 32 | heap.writeTo(System.out); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tools/src/main/java/org/adoptopenjdk/jheappo/model/HeapGraph.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.model; 2 | 3 | 4 | import org.adoptopenjdk.jheappo.io.*; 5 | import org.adoptopenjdk.jheappo.model.HeapGraphExtras.*; 6 | import org.adoptopenjdk.jheappo.objects.*; 7 | import org.neo4j.graphdb.*; 8 | import org.neo4j.graphdb.factory.GraphDatabaseFactory; 9 | import org.neo4j.io.fs.FileUtils; 10 | 11 | import java.io.*; 12 | import java.util.HashMap; 13 | import java.util.HashSet; 14 | 15 | public class HeapGraph { 16 | 17 | private static final int BATCH_SIZE = 50000; 18 | private final File path; 19 | 20 | private HashMap stringTable = new HashMap<>(); 21 | private HashMap clazzTable = new HashMap<>(); 22 | private HashMap clazzNodes = new HashMap<>(); 23 | private HashMap clazzNames = new HashMap<>(); 24 | private HashMap instanceNodes = new HashMap<>(); 25 | private HashMap oopTable = new HashMap<>(); 26 | private HashMap loadClassTable = new HashMap<>(); 27 | private HashSet rootStickClass = new HashSet<>(); 28 | private HashMap rootJNIGlobal = new HashMap<>(); 29 | private HashMap rootJNILocal = new HashMap<>(); 30 | private HashSet rootMonitorUsed = new HashSet<>(); 31 | private HashMap rootJavaFrame = new HashMap<>(); 32 | private HashMap rootThreadObject = new HashMap<>(); 33 | private HashMap primitiveArray = new HashMap<>(); 34 | private HashMap objectArray = new HashMap<>(); 35 | 36 | public HeapGraph(File path) { 37 | this.path = path; 38 | } 39 | 40 | public void populateFrom(HeapProfile heapDump) throws IOException { 41 | FileUtils.deleteRecursively(path); 42 | 43 | var db = new GraphDatabaseFactory().newEmbeddedDatabase(path); 44 | 45 | createIndexes(db); 46 | int count = 0; 47 | Transaction tx = db.beginTx(); 48 | 49 | try { 50 | HeapProfileHeader header = heapDump.readHeader(); 51 | System.out.println("Header: " + header.toString()); 52 | 53 | while (!heapDump.isAtHeapDumpEnd()) { 54 | HeapProfileRecord frame = heapDump.extract(); 55 | 56 | 57 | if (frame instanceof StackFrame) { 58 | } else if (frame instanceof StackTrace) { 59 | } else if (frame instanceof UTF8StringSegment) { 60 | var string = (((UTF8StringSegment) frame).toUtf8String()); 61 | stringTable.put(string.getId(), string); 62 | // out.write(Long.toString(string.getId()) + "->" + string.getString() + "\n"); 63 | } else if (frame instanceof LoadClass) { 64 | LoadClass loadClass = (LoadClass) frame; 65 | // loadClassTable.put(((LoadClass) frame).getClassObjectID(), (LoadClass) frame); //store mapping of class to class name. 66 | // out.write(frame.toString() + "\n"); 67 | clazzNames.put(loadClass.getClassObjectID(), loadClass.classNameStringID()); 68 | } else if (frame instanceof HeapDumpSegment) { 69 | while (!((HeapDumpSegment) frame).hasNext()) { 70 | var heapObject = ((HeapDumpSegment) frame).next(); 71 | if (heapObject == null) { 72 | System.out.println("parser error resolving type in HeapDumpSegment...."); 73 | continue; 74 | } 75 | if (heapObject instanceof ClassObject) { 76 | var classObject = (ClassObject) heapObject; 77 | clazzTable.put(heapObject.getId(), classObject); 78 | // clazzFile.write(heapObject.toString() + "\n"); 79 | Node node = mergeNode(db, clazzNodes, Labels.Class, heapObject.getId()); 80 | count++; 81 | node.setProperty("name", className(classObject.getId())); 82 | node.setProperty("size", classObject.instanceSizeInBytes()); 83 | 84 | for (int i = 0; i < classObject.fieldNameIndicies().length; i++) { 85 | long index = classObject.fieldNameIndicies()[i]; 86 | int type = classObject.fieldTypes()[i]; 87 | // todo string resolution 88 | node.setProperty(fieldName(index), type); 89 | } 90 | Node parent = mergeNode(db, clazzNodes, Labels.Class, classObject.superClassObjectID()); 91 | count++; 92 | node.createRelationshipTo(parent, Relationships.SUPERCLASS); 93 | count++; 94 | } else if (heapObject instanceof InstanceObject) { 95 | var instanceObject = (InstanceObject) heapObject; 96 | instanceObject.inflate(this.getClazzById(instanceObject.classObjectID())); 97 | // oopTable.put(heapObject.getId(), instanceObject); 98 | // instanceFile.write(heapObject.toString() + "\n"); 99 | Node node = mergeNode(db, instanceNodes, Labels.Instance, heapObject.getId()); 100 | count++; 101 | node.setProperty("stackSerial", instanceObject.stackTraceSerialNumber()); 102 | ClassObject classObject = getClazzById(instanceObject.classObjectID()); 103 | Node classNode = mergeNode(db, clazzNodes, Labels.Class, instanceObject.classObjectID()); 104 | count++; 105 | node.createRelationshipTo(classNode, HeapGraphExtras.Relationships.IS_CLASS); 106 | count++; 107 | 108 | for (int i = 0; i < classObject.fieldNameIndicies().length; i++) { 109 | long index = classObject.fieldNameIndicies()[i]; 110 | BasicDataTypeValue type = instanceObject.instanceFieldValues()[i]; 111 | switch (type.getType()) { 112 | case OBJECT: 113 | Node other = mergeNode(db, instanceNodes, Labels.Instance, (Long) type.getValue()); 114 | count++; 115 | Relationship rel = node.createRelationshipTo(other, Relationships.CONTAINS); 116 | count++; 117 | rel.setProperty("name", fieldName(index)); 118 | break; 119 | case BOOLEAN: 120 | case CHAR: 121 | case FLOAT: 122 | case DOUBLE: 123 | case BYTE: 124 | case SHORT: 125 | case INT: 126 | case LONG: 127 | node.setProperty(fieldName(index), type.getValue()); // todo type + value 128 | break; 129 | case ARRAY: 130 | break; 131 | case UNKNOWN: 132 | break; 133 | } 134 | } 135 | } 136 | /* 137 | else if (heapObject instanceof RootJNIGlobal) { 138 | rootJNIGlobal.put(heapObject.getId(), ((RootJNIGlobal) heapObject).getJNIGlobalRefID()); 139 | } else if (heapObject instanceof RootJNILocal) { 140 | rootJNILocal.put(heapObject.getId(), ((RootJNILocal) heapObject).getId()); 141 | } else if (heapObject instanceof PrimitiveArray) { 142 | primitiveArray.put(heapObject.getId(), (PrimitiveArray) heapObject); 143 | } else if (heapObject instanceof ObjectArray) { 144 | objectArray.put(heapObject.getId(), (ObjectArray) heapObject); 145 | } else if (heapObject instanceof RootJavaFrame) { 146 | rootJavaFrame.put(heapObject.getId(), (RootJavaFrame) heapObject); 147 | } else if (heapObject instanceof RootThreadObject) { 148 | rootThreadObject.put(heapObject.getId(), (RootThreadObject) heapObject); 149 | } else if (heapObject instanceof RootMonitorUsed) { 150 | rootMonitorUsed.add(heapObject.getId()); 151 | } else if (heapObject instanceof RootStickyClass) { 152 | rootStickClass.add(heapObject.getId()); 153 | } else 154 | System.out.println("missed : " + heapObject.toString()); 155 | */ 156 | } 157 | } else { 158 | // System.out.println("missed : " + frame.toString()); 159 | } 160 | if (count > BATCH_SIZE) { 161 | tx.success(); 162 | tx.close(); 163 | tx = db.beginTx(); 164 | count = 0; 165 | } 166 | } 167 | } finally { 168 | if (tx != null) { 169 | tx.success(); 170 | tx.close(); 171 | } 172 | db.shutdown(); 173 | } 174 | } 175 | 176 | private String className(long id) { 177 | 178 | return stringTable.get(clazzNames.get(id)).getString(); 179 | } 180 | 181 | private String fieldName(long index) { 182 | 183 | return "_" + stringTable.get(index).getString(); 184 | } 185 | 186 | private Node mergeNode(GraphDatabaseService db, HashMap cache, Labels type, long objectId) { 187 | 188 | return cache.computeIfAbsent(objectId, (id) -> { 189 | Node n = db.createNode(type); 190 | n.setProperty("id", id); 191 | return n; 192 | }); 193 | } 194 | 195 | private void createIndexes(GraphDatabaseService db) { 196 | try (Transaction tx = db.beginTx()) { 197 | db.schema().constraintFor(Labels.Instance).assertPropertyIsUnique("id").create(); 198 | db.schema().constraintFor(Labels.Class).assertPropertyIsUnique("id").create(); 199 | db.schema().indexFor(Labels.Class).on("name").create(); 200 | tx.success(); 201 | } 202 | } 203 | 204 | public ClassObject getClazzById(long cid) { 205 | 206 | return clazzTable.get(cid); 207 | } 208 | 209 | public void writeTo(PrintStream out) { 210 | //todo output data to stdout... 211 | } 212 | 213 | public void addInstanceObject(InstanceObject instanceObject) { 214 | oopTable.put(instanceObject.getId(), instanceObject); 215 | } 216 | 217 | public InstanceObject getInstanceObject(long id) { 218 | return oopTable.get(id); 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /tools/src/main/java/org/adoptopenjdk/jheappo/model/HeapGraphExtras.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.model; 2 | 3 | import org.neo4j.graphdb.Label; 4 | import org.neo4j.graphdb.RelationshipType; 5 | 6 | public class HeapGraphExtras { 7 | // Kotlin plays poorly with enums (which have a .name) that also implement an interface that requires name(), 8 | // so we use plain old Java for these 9 | 10 | enum Labels implements Label { 11 | Instance, Class 12 | } 13 | enum Relationships implements RelationshipType { 14 | CONTAINS, IS_CLASS, SUPERCLASS; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tools/src/main/java/org/adoptopenjdk/jheappo/model/JavaHeap.java: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.model; 2 | 3 | import org.adoptopenjdk.jheappo.io.*; 4 | import org.adoptopenjdk.jheappo.objects.*; 5 | 6 | import java.io.BufferedWriter; 7 | import java.io.FileWriter; 8 | import java.io.IOException; 9 | import java.io.PrintStream; 10 | import java.nio.file.Files; 11 | import java.nio.file.Path; 12 | import java.util.HashMap; 13 | import java.util.HashSet; 14 | 15 | public class JavaHeap { 16 | 17 | private HashMap stringTable = new HashMap<>(); 18 | private HashMap clazzTable = new HashMap<>(); 19 | private HashMap oopTable = new HashMap<>(); 20 | private HashMap loadClassTable = new HashMap<>(); 21 | private HashSet rootStickClass = new HashSet<>(); 22 | private HashMap rootJNIGlobal = new HashMap<>(); 23 | private HashMap rootJNILocal = new HashMap<>(); 24 | private HashSet rootMonitorUsed = new HashSet<>(); 25 | private HashMap rootJavaFrame = new HashMap<>(); 26 | private HashMap rootThreadObject = new HashMap<>(); 27 | private HashMap primitiveArray = new HashMap<>(); 28 | private HashMap objectArray = new HashMap<>(); 29 | 30 | private final Path outputDir; 31 | 32 | public JavaHeap(Path outputDir) { 33 | this.outputDir = outputDir; 34 | } 35 | 36 | public void populateFrom(HeapProfile heapDump) throws IOException { 37 | 38 | try (BufferedWriter out = Files.newBufferedWriter(outputDir.resolve("string.table")); 39 | BufferedWriter clazzFile = Files.newBufferedWriter(outputDir.resolve("class.table")); 40 | BufferedWriter instanceFile = Files.newBufferedWriter(outputDir.resolve("instance.table")); 41 | BufferedWriter loadClassFile = Files.newBufferedWriter(outputDir.resolve("loadClass.table")); 42 | ) { 43 | 44 | HeapProfileHeader header = heapDump.readHeader(); 45 | System.out.println("Header: " + header.toString()); 46 | 47 | while (!heapDump.isAtHeapDumpEnd()) { 48 | HeapProfileRecord frame = heapDump.extract(); 49 | if (frame instanceof StackFrame) { 50 | // Do Nothing 51 | } else if (frame instanceof StackTrace) { 52 | // Do Nothing 53 | } else if (frame instanceof UTF8StringSegment) { 54 | var string = ((UTF8StringSegment) frame).toUtf8String(); 55 | stringTable.put(string.getId(), string); 56 | out.write(string.getId() + "->" + string.getString() + "\n"); 57 | } else if (frame instanceof LoadClass) { 58 | loadClassTable.put(((LoadClass) frame).getClassObjectID(), (LoadClass) frame); //store mapping of class to class name. 59 | out.write(frame.toString() + "\n"); 60 | } else if (frame instanceof HeapDumpSegment) { 61 | while (!((HeapDumpSegment) frame).hasNext()) { 62 | HeapObject heapObject = ((HeapDumpSegment) frame).next(); 63 | if (heapObject == null) { 64 | System.out.println("parser error resolving type in HeapDumpSegment...."); 65 | continue; 66 | } 67 | 68 | if (heapObject instanceof ClassObject) { 69 | clazzTable.put(heapObject.getId(), (ClassObject) heapObject); 70 | clazzFile.write(heapObject.toString() + "\n"); 71 | } else if (heapObject instanceof InstanceObject) { 72 | var instanceObject = (InstanceObject) heapObject; 73 | instanceObject.inflate(this.getClazzById(instanceObject.classObjectID())); 74 | oopTable.put(heapObject.getId(), instanceObject); 75 | instanceFile.write(heapObject.toString() + "\n"); 76 | } else if (heapObject instanceof RootJNIGlobal) { 77 | rootJNIGlobal.put(heapObject.getId(), ((RootJNIGlobal) heapObject).getJNIGlobalRefID()); 78 | } else if (heapObject instanceof RootJNILocal) { 79 | rootJNILocal.put(heapObject.getId(), ((RootJNILocal) heapObject).getId()); 80 | } else if (heapObject instanceof PrimitiveArray) { 81 | primitiveArray.put(heapObject.getId(), (PrimitiveArray) heapObject); 82 | } else if (heapObject instanceof ObjectArray) { 83 | objectArray.put(heapObject.getId(), (ObjectArray) heapObject); 84 | } else if (heapObject instanceof RootJavaFrame) { 85 | rootJavaFrame.put(heapObject.getId(), (RootJavaFrame) heapObject); 86 | } else if (heapObject instanceof RootThreadObject) { 87 | rootThreadObject.put(heapObject.getId(), (RootThreadObject) heapObject); 88 | } else if (heapObject instanceof RootMonitorUsed) { 89 | rootMonitorUsed.add(heapObject.getId()); 90 | } else if (heapObject instanceof RootStickyClass) { 91 | rootStickClass.add(heapObject.getId()); 92 | } else { 93 | System.out.println("missed : " + heapObject.toString()); 94 | } 95 | } 96 | } else { 97 | System.out.println("missed : " + frame.toString()); 98 | } 99 | } 100 | } 101 | } 102 | 103 | public ClassObject getClazzById(long cid) { 104 | return clazzTable.get(cid); 105 | } 106 | 107 | public void writeTo(PrintStream out) { 108 | // TODO output data to stdout... 109 | } 110 | 111 | public void addInstanceObject(InstanceObject instanceObject) { 112 | oopTable.put(instanceObject.getId(), instanceObject); 113 | } 114 | 115 | public InstanceObject getInstanceObject(long id) { 116 | return oopTable.get(id); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /tools/src/test/java/org/adoptopenjdk/jheappo/heaptextoutput/HeapDumpTextTest.kt: -------------------------------------------------------------------------------- 1 | package org.adoptopenjdk.jheappo.heaptextoutput 2 | 3 | import org.adoptopenjdk.jheappo.io.HeapProfile 4 | import org.adoptopenjdk.jheappo.model.JavaHeap 5 | import org.junit.jupiter.api.Assertions.assertEquals 6 | import org.junit.jupiter.api.Test 7 | import org.junit.jupiter.api.io.TempDir 8 | import java.io.BufferedReader 9 | import java.io.InputStreamReader 10 | import java.nio.charset.StandardCharsets 11 | import java.nio.file.Files 12 | import java.nio.file.Path 13 | import java.nio.file.Paths 14 | 15 | class HeapDumpTextTest { 16 | @TempDir 17 | lateinit var tmp: Path; 18 | 19 | @Test 20 | fun textOutputFromHeapToolUnchanged() { 21 | HeapDumpTextTest::class.java.getResourceAsStream("heap.dump").use { stream -> 22 | JavaHeap(tmp).apply { 23 | populateFrom(HeapProfile.open(Paths.get("test"), stream)) 24 | } 25 | } 26 | 27 | val contents = tmp.toFile().list().toList() 28 | assertEquals(setOf("string.table", "class.table", "instance.table", "loadClass.table"), contents.toSet()) 29 | 30 | contents.forEach { filename -> 31 | val actual = Files.newBufferedReader(tmp.resolve(filename), StandardCharsets.UTF_8).readText() 32 | val expected = HeapDumpTextTest::class.java.getResourceAsStream(filename).use { stream -> 33 | InputStreamReader(stream, StandardCharsets.UTF_8).use { isr -> 34 | BufferedReader(isr).use { br -> 35 | br.readText() 36 | } 37 | } 38 | } 39 | 40 | assertEquals(expected, actual) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tools/src/test/resources/org/adoptopenjdk/jheappo/heaptextoutput/heap.dump: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdoptOpenJDK/jheappo/6af917c23830c989e7e11efbe2e534ebd2dbe8be/tools/src/test/resources/org/adoptopenjdk/jheappo/heaptextoutput/heap.dump -------------------------------------------------------------------------------- /tools/src/test/resources/org/adoptopenjdk/jheappo/heaptextoutput/loadClass.table: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdoptOpenJDK/jheappo/6af917c23830c989e7e11efbe2e534ebd2dbe8be/tools/src/test/resources/org/adoptopenjdk/jheappo/heaptextoutput/loadClass.table --------------------------------------------------------------------------------