├── .gitignore ├── LICENSE ├── README.md ├── heapunit ├── pom.xml └── src │ ├── main │ └── java │ │ └── org │ │ └── gridkit │ │ └── heapunit │ │ ├── HeapDumpProcuder.java │ │ ├── HeapImage.java │ │ ├── HeapInstance.java │ │ ├── HeapUnit.java │ │ └── SimpleHeapImage.java │ └── test │ └── java │ └── org │ └── gridkit │ └── heapunit │ ├── DumpSocketsExample.java │ └── HeapUnitTest.java └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .classpath 3 | .settings 4 | .project 5 | .factorypath 6 | .codenvy 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | HeapUnit 2 | ========= 3 | 4 | Test library to introspecting your Java heap via heap dumps. 5 | 6 | HeapUnit can 7 | 8 | - capture heap dump of own JVM 9 | - scan content of dump 10 | - reconstruct Java objects from heap dump 11 | 12 | Example 13 | --------- 14 | Code snippet below dumps TCP Socket instances found in heap 15 | 16 | HeapImage hi = HeapUnit.captureHeap(); 17 | 18 | for(HeapInstance i: hi.instances(SocketImpl.class)) { 19 | // fd field in SocketImpl class is nullified when socket gets closed 20 | boolean open = i.value("fd") != null; 21 | System.out.println(i.rehydrate() + (open ? " - open" : " - closed")); 22 | } 23 | 24 | Full source is [here](heapunit/src/test/java/org/gridkit/heapunit/DumpSocketsExample.java) 25 | 26 | Maven artifact 27 | --------- 28 | 29 | HeapUnit and dependencies is available in Maven Central Repo 30 | 31 | 32 | org.gridkit.heapunit 33 | heapunit 34 | 0.2 35 | 36 | -------------------------------------------------------------------------------- /heapunit/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | 21 | 4.0.0 22 | 23 | 24 | org.gridkit.heapunit 25 | heapunit-pom 26 | 0.3-SNAPSHOT 27 | ../pom.xml 28 | 29 | 30 | heapunit 31 | ${project.groupId}::${project.artifactId} 32 | HeapUnit heap capture and introspection for test purposes 33 | 34 | 35 | 36 | org.gridkit.lab 37 | jvm-attach-api 38 | 1.2 39 | 40 | 41 | org.gridkit.jvmtool 42 | hprof-heap 43 | 0.4.1 44 | 45 | 46 | org.objenesis 47 | objenesis 48 | 2.3 49 | 50 | 51 | junit 52 | junit 53 | 4.11 54 | test 55 | 56 | 57 | org.assertj 58 | assertj-core 59 | 1.6.0 60 | test 61 | 62 | 63 | 64 | 65 | 66 | 67 | org.apache.maven.plugins 68 | maven-compiler-plugin 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /heapunit/src/main/java/org/gridkit/heapunit/HeapDumpProcuder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2016 Alexey Ragozin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.gridkit.heapunit; 17 | 18 | import java.io.File; 19 | import java.io.IOException; 20 | import java.lang.management.ManagementFactory; 21 | 22 | import org.gridkit.lab.jvm.attach.HeapDumper; 23 | import org.netbeans.lib.profiler.heap.Heap; 24 | import org.netbeans.lib.profiler.heap.HeapFactory; 25 | 26 | class HeapDumpProcuder { 27 | 28 | private static int PID; 29 | static { 30 | String pid = ManagementFactory.getRuntimeMXBean().getName(); 31 | PID = Integer.valueOf(pid.substring(0, pid.indexOf('@'))); 32 | } 33 | 34 | 35 | public static Heap makeHeapDump(String path, long timeoutMs) throws IOException { 36 | File file = new File(path); 37 | if (file.getParentFile() != null && file.getParentFile().mkdirs()); 38 | if (file.exists() && file.delete()); 39 | System.out.println("Generating heap dump: " + path); 40 | System.out.println(HeapDumper.dumpLive(PID, path, timeoutMs)); 41 | 42 | Heap heap = HeapFactory.createFastHeap(file); 43 | 44 | return heap; 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /heapunit/src/main/java/org/gridkit/heapunit/HeapImage.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2016 Alexey Ragozin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.gridkit.heapunit; 17 | 18 | import org.netbeans.lib.profiler.heap.Heap; 19 | import org.netbeans.lib.profiler.heap.Instance; 20 | 21 | public interface HeapImage { 22 | 23 | public long instanceCount(Class c); 24 | 25 | public long instanceCount(String selector); 26 | 27 | public HeapInstance instance(String selector); 28 | 29 | public Iterable instances(String selector); 30 | 31 | public Iterable instances(Class c); 32 | 33 | public T rehydrate(Instance instance); 34 | 35 | public T rehydrateFirst(String selector); 36 | 37 | public Iterable rehydrate(String selector); 38 | 39 | public Iterable rehydrate(Class c); 40 | 41 | public Heap getHeap(); 42 | 43 | } 44 | -------------------------------------------------------------------------------- /heapunit/src/main/java/org/gridkit/heapunit/HeapInstance.java: -------------------------------------------------------------------------------- 1 | package org.gridkit.heapunit; 2 | 3 | import org.gridkit.jvmtool.heapdump.HeapWalker; 4 | import org.netbeans.lib.profiler.heap.Instance; 5 | 6 | /** 7 | * This is reference to Java object in heap. 8 | *

9 | * {@link HeapImage} wraps {@link Instance} and add some extra utility. 10 | * 11 | * @author Alexey Ragozin (alexey.ragozin@gmail.com) 12 | */ 13 | public class HeapInstance { 14 | 15 | private final SimpleHeapImage heap; 16 | private final Instance instance; 17 | 18 | public HeapInstance(SimpleHeapImage heap, Instance instance) { 19 | this.heap = heap; 20 | this.instance = instance; 21 | } 22 | 23 | /** 24 | * Retries first element matched by path expression. 25 | *

26 | * This method do not do rehydration automatically {@link String} 27 | *

  • {@link String}
  • 28 | *
  • primitive types
  • 29 | *
  • box types
  • 30 | *
  • primitive arrays
  • 31 | * would be converted to Java objects, other values would be returned as {@link HeapInstance}. 32 | *

    33 | * This method is convenient to access field values of instance. 34 | */ 35 | @SuppressWarnings("unchecked") 36 | public T value(String path) { 37 | Object v = HeapWalker.valueOf(instance, path); 38 | if (v instanceof Instance) { 39 | return (T) new HeapInstance(heap, (Instance) v); 40 | } 41 | else { 42 | return (T) v; 43 | } 44 | } 45 | 46 | public T rehydrate() { 47 | return heap.rehydrate(instance); 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return instance.getJavaClass().getName() + "@" + instance.getInstanceId(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /heapunit/src/main/java/org/gridkit/heapunit/HeapUnit.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2016 Alexey Ragozin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.gridkit.heapunit; 17 | 18 | import java.io.IOException; 19 | import java.util.concurrent.TimeUnit; 20 | 21 | public class HeapUnit { 22 | 23 | public static HeapImage captureHeap() throws IOException { 24 | String path = "target/heapunit/" + System.currentTimeMillis() + ".hprof"; 25 | return new SimpleHeapImage(HeapDumpProcuder.makeHeapDump(path, TimeUnit.SECONDS.toMillis(60))); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /heapunit/src/main/java/org/gridkit/heapunit/SimpleHeapImage.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2016 Alexey Ragozin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.gridkit.heapunit; 17 | 18 | import java.lang.reflect.Array; 19 | import java.lang.reflect.Field; 20 | import java.util.Collections; 21 | import java.util.HashMap; 22 | import java.util.HashSet; 23 | import java.util.Iterator; 24 | import java.util.LinkedHashMap; 25 | import java.util.List; 26 | import java.util.Map; 27 | import java.util.NoSuchElementException; 28 | import java.util.Set; 29 | 30 | import org.gridkit.jvmtool.heapdump.HeapWalker; 31 | import org.gridkit.jvmtool.heapdump.RefSet; 32 | import org.netbeans.lib.profiler.heap.FieldValue; 33 | import org.netbeans.lib.profiler.heap.Heap; 34 | import org.netbeans.lib.profiler.heap.Instance; 35 | import org.netbeans.lib.profiler.heap.JavaClass; 36 | import org.netbeans.lib.profiler.heap.ObjectArrayInstance; 37 | import org.netbeans.lib.profiler.heap.ObjectFieldValue; 38 | import org.objenesis.Objenesis; 39 | import org.objenesis.ObjenesisStd; 40 | import org.objenesis.instantiator.ObjectInstantiator; 41 | 42 | public class SimpleHeapImage implements HeapImage { 43 | 44 | private static final Map> PTYPES = new HashMap>(); 45 | static { 46 | PTYPES.put("void", void.class); 47 | PTYPES.put("byte", byte.class); 48 | PTYPES.put("short", short.class); 49 | PTYPES.put("char", char.class); 50 | PTYPES.put("int", int.class); 51 | PTYPES.put("long", long.class); 52 | PTYPES.put("float", float.class); 53 | PTYPES.put("double", double.class); 54 | } 55 | 56 | private Objenesis objeneis = new ObjenesisStd(); 57 | private final Map rehydrationCache = new HashMap(); 58 | private final Map> inheritanceCache = new HashMap>(); 59 | private final Map instantiators = new HashMap(); 60 | 61 | private final Heap heap; 62 | 63 | public SimpleHeapImage(Heap heap) { 64 | this.heap = heap; 65 | } 66 | 67 | @Override 68 | public long instanceCount(Class c) { 69 | long cc = 0; 70 | Set types = getSubclasses(c); 71 | for(Instance i: heap.getAllInstances()) { 72 | if (types.contains(i.getJavaClass())) { 73 | cc++; 74 | } 75 | } 76 | return cc; 77 | } 78 | 79 | @Override 80 | public long instanceCount(String selector) { 81 | long cc = 0; 82 | for(@SuppressWarnings("unused") HeapInstance i: iterate(selector)) { 83 | cc++; 84 | } 85 | return cc; 86 | } 87 | 88 | @Override 89 | public HeapInstance instance(String selector) { 90 | Iterator it = iterate(selector).iterator(); 91 | return it.hasNext() ? it.next() : null; 92 | } 93 | 94 | @Override 95 | public Iterable instances(String selector) { 96 | return iterate(selector); 97 | } 98 | 99 | @Override 100 | public Iterable instances(Class c) { 101 | final Set types = getSubclasses(c); 102 | return new Iterable() { 103 | 104 | @Override 105 | public Iterator iterator() { 106 | final Iterator it = heap.getAllInstances().iterator(); 107 | return new AbsIt() { 108 | 109 | @Override 110 | protected HeapInstance seek() { 111 | while(it.hasNext()) { 112 | Instance i = it.next(); 113 | if (types == null || types.contains(i.getJavaClass())) { 114 | return new HeapInstance(SimpleHeapImage.this, i); 115 | } 116 | } 117 | return null; 118 | } 119 | }; 120 | } 121 | }; 122 | } 123 | 124 | private Iterable iterate(final String selector) { 125 | final Set types = filterTypes(selector); 126 | 127 | if (types != null && types.isEmpty()) { 128 | return Collections.emptyList(); 129 | } 130 | 131 | return new Iterable() { 132 | 133 | @Override 134 | public Iterator iterator() { 135 | 136 | final RefSet visited = new RefSet(); 137 | final Iterator instances = heap.getAllInstances().iterator(); 138 | 139 | return new AbsIt() { 140 | 141 | Iterator walker = null; 142 | 143 | @Override 144 | protected HeapInstance seek() { 145 | while(true) { 146 | if (walker == null) { 147 | if (instances.hasNext()) { 148 | Instance i = instances.next(); 149 | if (types == null || types.contains(i.getJavaClass())) { 150 | walker = HeapWalker.walk(i, selector).iterator(); 151 | } 152 | } 153 | else { 154 | return null; 155 | } 156 | } 157 | else if (walker.hasNext()) { 158 | Instance i = walker.next(); 159 | long a = i.getInstanceId(); 160 | if (!visited.get(a)) { 161 | visited.set(a, true); 162 | return new HeapInstance(SimpleHeapImage.this, i); 163 | } 164 | } 165 | else { 166 | walker = null; 167 | } 168 | } 169 | } 170 | }; 171 | } 172 | }; 173 | } 174 | 175 | protected Set filterTypes(String selector) { 176 | if (selector.startsWith("(")) { 177 | int n = selector.indexOf(")"); 178 | String tf = selector.substring(1, n); 179 | return HeapWalker.filterTypes(tf, heap.getAllClasses()); 180 | } 181 | else { 182 | return null; 183 | } 184 | } 185 | 186 | private Set getSubclasses(Class c) { 187 | String name = c.getName(); 188 | if (inheritanceCache.containsKey(name)) { 189 | return inheritanceCache.get(name); 190 | } 191 | else { 192 | Set result = new HashSet(); 193 | 194 | for(JavaClass jc: heap.getAllClasses()) { 195 | if (isInherited(jc, c)) { 196 | result.add(jc); 197 | } 198 | } 199 | 200 | inheritanceCache.put(name, result); 201 | return result; 202 | } 203 | } 204 | 205 | private boolean isInherited(JavaClass jc, Class c) { 206 | String dcname = jc.getName(); 207 | String rcname = className(c); 208 | if (dcname.equals(rcname)) { 209 | return true; 210 | } 211 | else { 212 | Class x = classForName(dcname); 213 | if (x != null && c.isAssignableFrom(x)) { 214 | return true; 215 | } 216 | } 217 | return false; 218 | } 219 | 220 | private Class classForName(String dcname) { 221 | if (dcname.endsWith("[]")) { 222 | Class x = classForName(dcname.substring(0, dcname.length() - 2)); 223 | if (x != null) { 224 | x = Array.newInstance(x, 0).getClass(); 225 | } 226 | return x; 227 | } 228 | else { 229 | if (PTYPES.containsKey(dcname)) { 230 | return PTYPES.get(dcname); 231 | } 232 | try { 233 | return Class.forName(dcname); 234 | } 235 | catch(Exception e) { 236 | // ignore 237 | } 238 | catch(NoClassDefFoundError e) { 239 | // ignore 240 | } 241 | return null; 242 | } 243 | } 244 | 245 | private String className(Class c) { 246 | if (c.isArray()) { 247 | return className(c.getComponentType()) + "[]"; 248 | } 249 | else { 250 | return c.getName(); 251 | } 252 | } 253 | 254 | @SuppressWarnings("unchecked") 255 | @Override 256 | public T rehydrate(Instance instance) { 257 | long id = instance.getInstanceId(); 258 | if (rehydrationCache.containsKey(id)) { 259 | return (T) rehydrationCache.get(id); 260 | } 261 | else { 262 | JavaClass jc = instance.getJavaClass(); 263 | Rehydrator rh = rehydrator(jc); 264 | if (rh != null) { 265 | try { 266 | return (T) rh.rehydrateInstance(instance); 267 | } catch (Exception e) { 268 | } catch (InstantiationError e) { 269 | } 270 | return (T) rehydrationCache.get(id); 271 | } 272 | else { 273 | rehydrationCache.put(id, null); 274 | return null; 275 | } 276 | } 277 | } 278 | 279 | private Rehydrator rehydrator(JavaClass jc) { 280 | if (instantiators.containsKey(jc.getName())) { 281 | return instantiators.get(jc.getName()); 282 | } 283 | else { 284 | Rehydrator rh = null; 285 | Class c = classForName(jc.getName()); 286 | if (c == String.class || (c.isArray() && c.getComponentType().isPrimitive())) { 287 | rh = new SimpleRehydrator(); 288 | } 289 | else if (c.isArray()) { 290 | rh = new ArrayRehydrator(c); 291 | } 292 | else { 293 | try { 294 | rh = new ObjenesisRehydrator(c, jc); 295 | } 296 | catch(Exception e) { 297 | // ignore 298 | } 299 | } 300 | instantiators.put(jc.getName(), rh); 301 | return rh; 302 | } 303 | } 304 | 305 | @Override 306 | public T rehydrateFirst(String selector) { 307 | HeapInstance hp = instance(selector); 308 | return hp == null ? null : hp.rehydrate(); 309 | } 310 | 311 | @Override 312 | public Iterable rehydrate(final String selector) { 313 | return new Iterable() { 314 | 315 | @Override 316 | public Iterator iterator() { 317 | final Iterator it = instances(selector).iterator(); 318 | return new AbsIt() { 319 | 320 | @Override 321 | @SuppressWarnings("unchecked") 322 | protected T seek() { 323 | return it.hasNext() ? (T)it.next().rehydrate() : null; 324 | } 325 | }; 326 | } 327 | }; 328 | } 329 | 330 | @Override 331 | public Iterable rehydrate(final Class c) { 332 | return new Iterable() { 333 | 334 | @Override 335 | public Iterator iterator() { 336 | final Iterator it = instances(c).iterator(); 337 | return new AbsIt() { 338 | 339 | @Override 340 | protected T seek() { 341 | return it.hasNext() ? it.next().rehydrate() : null; 342 | } 343 | }; 344 | } 345 | }; 346 | } 347 | 348 | @Override 349 | public Heap getHeap() { 350 | return heap; 351 | } 352 | 353 | private static abstract class AbsIt implements Iterator { 354 | 355 | T next; 356 | 357 | protected abstract T seek(); 358 | 359 | @Override 360 | public boolean hasNext() { 361 | if (next == null) { 362 | next = seek(); 363 | } 364 | return next != null; 365 | } 366 | 367 | @Override 368 | public T next() { 369 | if (!hasNext()) { 370 | throw new NoSuchElementException(); 371 | } 372 | T r = next; 373 | next = null; 374 | return r; 375 | } 376 | 377 | @Override 378 | public void remove() { 379 | throw new UnsupportedOperationException(); 380 | } 381 | } 382 | 383 | private abstract class Rehydrator { 384 | 385 | public abstract Object rehydrateInstance(Instance i) throws Exception; 386 | 387 | } 388 | 389 | private class SimpleRehydrator extends Rehydrator { 390 | 391 | @Override 392 | public Object rehydrateInstance(Instance i) { 393 | Object v = HeapWalker.valueOf(i); 394 | rehydrationCache.put(i.getInstanceId(), v); 395 | return v; 396 | } 397 | } 398 | 399 | private class ArrayRehydrator extends Rehydrator { 400 | 401 | Class ctype; 402 | 403 | public ArrayRehydrator(Class type) { 404 | this.ctype = type.getComponentType(); 405 | } 406 | 407 | @Override 408 | public Object rehydrateInstance(Instance ii) { 409 | ObjectArrayInstance oai = (ObjectArrayInstance) ii; 410 | int len = oai.getLength(); 411 | Object a = Array.newInstance(ctype, len); 412 | rehydrationCache.put(ii.getInstanceId(), a); 413 | List values = oai.getValues(); 414 | for(int i = 0; i != len; ++i) { 415 | Array.set(a, i, rehydrate(values.get(i))); 416 | } 417 | return a; 418 | } 419 | } 420 | 421 | private class ObjenesisRehydrator extends Rehydrator { 422 | 423 | private ObjectInstantiator instantiator; 424 | private Map fields = new LinkedHashMap(); 425 | 426 | public ObjenesisRehydrator(Class type, JavaClass jc) { 427 | instantiator = objeneis.getInstantiatorOf(type); 428 | initFields(type, jc); 429 | } 430 | 431 | private void initFields(Class c, JavaClass jc) { 432 | 433 | if (jc.getSuperClass() != null) { 434 | initFields(c.getSuperclass(), jc.getSuperClass()); 435 | } 436 | 437 | for(org.netbeans.lib.profiler.heap.Field jf: jc.getFields()) { 438 | if (!jf.isStatic()) { 439 | try { 440 | Field rf = c.getDeclaredField(jf.getName()); 441 | rf.setAccessible(true); 442 | String name = jf.getDeclaringClass().getName() + "#" + jf.getName(); 443 | fields.put(name, rf); 444 | } 445 | catch(Exception e) { 446 | // ignore 447 | } 448 | } 449 | } 450 | } 451 | 452 | @Override 453 | public Object rehydrateInstance(Instance i) throws Exception { 454 | 455 | Object v = instantiator.newInstance(); 456 | 457 | rehydrationCache.put(i.getInstanceId(), v); 458 | 459 | for(FieldValue fv: i.getFieldValues()) { 460 | try { 461 | String name = fv.getField().getDeclaringClass().getName() + "#" + fv.getField().getName(); 462 | Field f = fields.get(name); 463 | if (f != null) { 464 | if (f.getType().isPrimitive()) { 465 | setPrimitive(v, f, fv.getValue()); 466 | } 467 | else { 468 | f.set(v, rehydrate(((ObjectFieldValue)fv).getInstance())); 469 | } 470 | } 471 | } 472 | catch(Exception e) { 473 | // ignore 474 | } 475 | } 476 | 477 | return v; 478 | } 479 | 480 | private void setPrimitive(Object o, Field f, String value) throws IllegalArgumentException, IllegalAccessException { 481 | if (f.getType() == boolean.class) { 482 | f.set(o, Boolean.valueOf(value)); 483 | } 484 | else if (f.getType() == byte.class) { 485 | f.set(o, Byte.valueOf(value)); 486 | } 487 | else if (f.getType() == char.class) { 488 | f.set(o, value.charAt(0)); 489 | } 490 | else if (f.getType() == short.class) { 491 | f.set(o, Short.valueOf(value)); 492 | } 493 | else if (f.getType() == int.class) { 494 | f.set(o, Integer.valueOf(value)); 495 | } 496 | else if (f.getType() == long.class) { 497 | f.set(o, Long.valueOf(value)); 498 | } 499 | else if (f.getType() == float.class) { 500 | f.set(o, Float.valueOf(value)); 501 | } 502 | else if (f.getType() == double.class) { 503 | f.set(o, Double.valueOf(value)); 504 | } 505 | } 506 | } 507 | } 508 | -------------------------------------------------------------------------------- /heapunit/src/test/java/org/gridkit/heapunit/DumpSocketsExample.java: -------------------------------------------------------------------------------- 1 | package org.gridkit.heapunit; 2 | 3 | import java.io.IOException; 4 | import java.net.InetSocketAddress; 5 | import java.net.ServerSocket; 6 | import java.net.Socket; 7 | import java.net.SocketAddress; 8 | import java.net.SocketImpl; 9 | 10 | import org.junit.Test; 11 | 12 | public class DumpSocketsExample { 13 | 14 | @Test 15 | public void printSockets() throws IOException { 16 | 17 | ServerSocket ss = new ServerSocket(); 18 | ss.bind(sock(5000)); 19 | 20 | Socket s1 = new Socket(); 21 | Socket s2 = new Socket(); 22 | 23 | s1.connect(sock(5000)); 24 | s2.connect(sock(5000)); 25 | 26 | ss.close(); 27 | s1.close(); 28 | // s2 remains unclosed 29 | 30 | HeapImage hi = HeapUnit.captureHeap(); 31 | 32 | for(HeapInstance i: hi.instances(SocketImpl.class)) { 33 | // fd field in SocketImpl class is nullified when socket gets closed 34 | boolean open = i.value("fd") != null; 35 | System.out.println(i.rehydrate() + (open ? " - open" : " - closed")); 36 | } 37 | } 38 | 39 | private SocketAddress sock(int port) { 40 | return new InetSocketAddress("127.1.2.3", port); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /heapunit/src/test/java/org/gridkit/heapunit/HeapUnitTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2016 Alexey Ragozin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.gridkit.heapunit; 17 | 18 | import static org.junit.Assert.assertEquals; 19 | 20 | import java.io.IOException; 21 | import java.util.Arrays; 22 | import java.util.List; 23 | 24 | import org.junit.Assert; 25 | import org.junit.Test; 26 | 27 | public class HeapUnitTest { 28 | 29 | @Test 30 | public void test() throws IOException { 31 | 32 | @SuppressWarnings("unused") 33 | TestInstance tt = new TestInstance(); 34 | 35 | HeapImage hi = HeapUnit.captureHeap(); 36 | 37 | System.out.println("String count: " + hi.instanceCount(String.class)); 38 | Assert.assertTrue(hi.instanceCount(String.class) > 0); 39 | 40 | System.out.println("List count: " + hi.instanceCount(List.class)); 41 | Assert.assertTrue(hi.instanceCount(List.class) > 0); 42 | 43 | System.out.println("TestInstance count: " + hi.instanceCount(TestInstance.class)); 44 | assertEquals(1, hi.instanceCount(TestInstance.class)); 45 | 46 | System.out.println("(**$TestInstance) count: " + hi.instanceCount("(**$TestInstance)")); 47 | assertEquals(1, hi.instanceCount("(**$TestInstance)")); 48 | 49 | System.out.println("char[] count: " + hi.instanceCount(char[].class)); 50 | Assert.assertTrue(hi.instanceCount(char[].class) > 0); 51 | 52 | System.out.println("Object[] count: " + hi.instanceCount(Object[].class)); 53 | Assert.assertTrue(hi.instanceCount(Object[].class) > 0); 54 | 55 | TestInstance t2 = hi.rehydrateFirst("(**$TestInstance)"); 56 | 57 | System.out.println(t2); 58 | 59 | HeapInstance ti = hi.instance("(**$TestInstance)"); 60 | 61 | assertEquals(true, ti.value("bval")); 62 | assertEquals(10, ti.value("ival")); 63 | assertEquals(1.1d, ti.value("dval")); 64 | } 65 | 66 | 67 | static class TestInstance { 68 | 69 | boolean bval = true; 70 | int ival = 10; 71 | double dval = 1.1d; 72 | byte[] array = new byte[0]; 73 | String[] stringArray = new String[]{}; 74 | List list = Arrays.asList("1", "2", "3"); 75 | 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | 21 | 4.0.0 22 | 23 | 24 | org.gridkit.lab 25 | grid-lab-pom 26 | 2 27 | 28 | 29 | org.gridkit.heapunit 30 | heapunit-pom 31 | 0.3-SNAPSHOT 32 | ${project.groupId}::${project.artifactId} 33 | HeapUnit parent pom 34 | pom 35 | 36 | 37 | heapunit 38 | 39 | 40 | 41 | scm:git:https://github.com/aragozin/heapunit.git 42 | scm:git:https://github.com/aragozin/heapunit.git 43 | https://github.com/aragozin/heapunit 44 | HEAD 45 | 46 | 47 | 48 | 49 | The Apache Software License, Version 2.0 50 | http://www.apache.org/licenses/LICENSE-2.0.txt 51 | repo 52 | 53 | 54 | 55 | 56 | 57 | alexey.ragozin 58 | Alexey Ragozin 59 | alexey.ragozin@gmail.com 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | org.apache.maven.plugins 68 | maven-compiler-plugin 69 | 3.0 70 | 71 | 1.6 72 | 1.6 73 | 74 | 75 | 76 | org.apache.maven.plugins 77 | maven-release-plugin 78 | 2.4 79 | 80 | true 81 | true 82 | 83 | 84 | 85 | org.apache.maven.plugins 86 | maven-surefire-plugin 87 | 2.14 88 | 89 | 90 | org.apache.maven.plugins 91 | maven-javadoc-plugin 92 | 2.9 93 | 94 | 95 | attach-javadoc 96 | 97 | jar 98 | 99 | 100 | 101 | 102 | 103 | org.apache.maven.plugins 104 | maven-source-plugin 105 | 2.2.1 106 | 107 | 108 | attach-source 109 | 110 | jar 111 | 112 | 113 | 114 | 115 | 116 | org.apache.maven.plugins 117 | maven-resources-plugin 118 | 2.6 119 | 120 | UTF-8 121 | 122 | 123 | 124 | 125 | 126 | 127 | --------------------------------------------------------------------------------