├── .gitignore
├── Jenkinsfile
├── LICENSE.txt
├── README.md
├── pom.xml
└── src
├── main
├── java
│ └── objectexplorer
│ │ ├── Chain.java
│ │ ├── InstrumentationGrabber.java
│ │ ├── MemoryMeasurer.java
│ │ ├── ObjectExplorer.java
│ │ ├── ObjectGraphMeasurer.java
│ │ ├── ObjectSerializer.java
│ │ └── ObjectVisitor.java
└── resources
│ └── META-INF
│ └── MANIFEST.MF
└── test
└── java
├── memorymeasurer
└── ElementCostOfDataStructures.java
└── objectexplorer
└── ObjectSerializerTest.java
/.gitignore:
--------------------------------------------------------------------------------
1 | build/
2 | dist/
3 | /target/
4 |
--------------------------------------------------------------------------------
/Jenkinsfile:
--------------------------------------------------------------------------------
1 | node {
2 | def mvnHome = tool 'M3'
3 | env.JAVA_HOME="${tool 'jdk-oracle-8'}"
4 | env.PATH="${env.JAVA_HOME}/bin:${mvnHome}/bin:${env.PATH}"
5 |
6 | stage 'Clone'
7 | checkout scm
8 |
9 | stage 'Build and Test'
10 | sh "mvn -B clean install"
11 |
12 | stage 'Deploy'
13 | sh "mvn -s ${env.HOME}/usethesource-maven-settings.xml -B deploy"
14 |
15 | stage 'Archive'
16 | step([$class: 'ArtifactArchiver', artifacts: '**/target/*.jar', fingerprint: true])
17 | step([$class: 'JUnitResultArchiver', testResults: '**/target/surefire-reports/TEST-*.xml'])
18 | }
19 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
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 [2009] [Dimitrios Andreou]
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.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ObjectExplorer
2 |
3 | Originally authored by Dimitris Andreou (jim.andreou@gmail.com).
4 |
5 | See package javadocs for information.
6 |
7 | Quick tip: To use the MemoryMeasurer (to measure the footprint of an object
8 | graph in bytes), this parameter needs to be passed to th VM:
9 | -javaagent:path/to/object-explorer.jar
10 |
11 | # Origin
12 |
13 | This library was forked when it used to exist on Google Code only. In the meantime the original author moved the code to github as well ([DimitrisAndreou/memory-measurer](https://github.com/DimitrisAndreou/memory-measurer)), however it appears that the library is not actively maintained any more.
14 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | 4.0.0
3 |
4 | com.github.msteindorfer
5 | memory-measurer
6 | 0.1.0-SNAPSHOT
7 | jar
8 |
9 |
10 | scm:git:https://github.com/msteindorfer/memory-measurer.git
11 | HEAD
12 |
13 |
14 |
15 |
16 |
17 | usethesource-releases
18 | http://nexus.rascal-mpl.org/repository/maven-releases/
19 |
20 |
21 | usethesource-snapshots
22 | http://nexus.rascal-mpl.org/repository/maven-snapshots/
23 |
24 |
25 |
26 |
27 |
28 |
29 | usethesource
30 | http://nexus.rascal-mpl.org/repository/maven-public/
31 |
32 |
33 |
34 |
35 | UTF-8
36 | 1.8
37 | 1.8
38 |
39 |
40 |
41 |
42 |
43 | org.apache.maven.plugins
44 | maven-jar-plugin
45 | 3.0.2
46 |
47 |
48 |
49 | objectexplorer.InstrumentationGrabber
50 |
51 |
52 |
57 |
60 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | com.google.guava
72 | guava
73 | r09
74 |
75 |
76 | com.google.code.findbugs
77 | jsr305
78 | 3.0.1
79 |
80 |
81 |
82 |
83 | junit
84 | junit
85 | 4.11
86 | test
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/src/main/java/objectexplorer/Chain.java:
--------------------------------------------------------------------------------
1 | package objectexplorer;
2 |
3 | import com.google.common.base.Preconditions;
4 | import java.lang.reflect.Field;
5 | import java.util.ArrayDeque;
6 | import java.util.Deque;
7 | import java.util.Iterator;
8 | import javax.annotation.Nonnull;
9 | import javax.annotation.Nullable;
10 |
11 | /**
12 | * A chain of references, which starts at a root object and leads to a
13 | * particular value (either an object or a primitive).
14 | *
15 | * @author andreou
16 | */
17 | public abstract class Chain {
18 | private final Object value;
19 | private final Chain parent;
20 |
21 | Chain(Chain parent, Object value) {
22 | this.parent = parent;
23 | this.value = value;
24 | }
25 |
26 | static Chain root(Object value) {
27 | return new Chain(null, Preconditions.checkNotNull(value)) {
28 | @Override
29 | public Class> getValueType() {
30 | return getValue().getClass();
31 | }
32 | };
33 | }
34 |
35 | FieldChain appendField(Field field, Object value) {
36 | return new FieldChain(this, Preconditions.checkNotNull(field), value);
37 | }
38 |
39 | ArrayIndexChain appendArrayIndex(int arrayIndex, Object value) {
40 | return new ArrayIndexChain(this, arrayIndex, value);
41 | }
42 |
43 | /**
44 | * Returns whether this chain has a parent. This returns false only when
45 | * this chain represents the root object itself.
46 | */
47 | public boolean hasParent() {
48 | return parent != null;
49 | }
50 |
51 | /**
52 | * Returns the parent chain, from which this chain was created.
53 | * @throws IllegalStateException if {@code !hasParent()}, then an
54 | */
55 | public @Nonnull Chain getParent() {
56 | Preconditions.checkState(parent != null, "This is the root value, it has no parent");
57 | return parent;
58 | }
59 |
60 | /**
61 | * Returns the value that this chain leads to. If the value is a primitive,
62 | * a wrapper object is returned instead.
63 | */
64 | public @Nullable Object getValue() {
65 | return value;
66 | }
67 |
68 | public abstract @Nonnull Class> getValueType();
69 |
70 | /**
71 | * Returns whether the connection of the parent chain and this chain is
72 | * through a field (of the getParent().getValue().getClass() class).
73 | */
74 | public boolean isThroughField() {
75 | return false;
76 | }
77 |
78 | /**
79 | * Returns whether the connection of the parent chain and this chain is
80 | * through an array index, i.e. the parent leads to an array, and this
81 | * chain leads to an element of that array.
82 | */
83 | public boolean isThroughArrayIndex() {
84 | return false;
85 | }
86 |
87 | /**
88 | * Returns whether the value of this chain represents a primitive.
89 | */
90 | public boolean isPrimitive() {
91 | return getValueType().isPrimitive();
92 | }
93 |
94 | /**
95 | * Returns the root object of this chain.
96 | */
97 | public @Nonnull Object getRoot() {
98 | Chain current = this;
99 | while (current.hasParent()) {
100 | current = current.getParent();
101 | }
102 | return current.getValue();
103 | }
104 |
105 | Deque reverse() {
106 | Deque reverseChain = new ArrayDeque(8);
107 | Chain current = this;
108 | reverseChain.addFirst(current);
109 | while (current.hasParent()) {
110 | current = current.getParent();
111 | reverseChain.addFirst(current);
112 | }
113 | return reverseChain;
114 | }
115 |
116 | @Override public String toString() {
117 | StringBuilder sb = new StringBuilder(32);
118 |
119 | Iterator it = reverse().iterator();
120 | // sb.append(it.next().getValue());
121 | it.next(); sb.append("rootObject");
122 | while (it.hasNext()) {
123 | sb.append("->");
124 | Chain current = it.next();
125 | if (current.isThroughField()) {
126 | sb.append(((FieldChain)current).getField().getName());
127 | } else if (current.isThroughArrayIndex()) {
128 | sb.append("[").append(((ArrayIndexChain)current).getArrayIndex()).append("]");
129 | }
130 | }
131 | return sb.toString();
132 | }
133 |
134 | /*
135 | * Does reuse an existing StringBuffer instead of allocating a new one
136 | */
137 | public void toString(StringBuffer sb) {
138 | Iterator it = reverse().iterator();
139 | // sb.append(it.next().getValue());
140 | it.next(); sb.append("rootObject");
141 | while (it.hasNext()) {
142 | sb.append("->");
143 | Chain current = it.next();
144 | if (current.isThroughField()) {
145 | sb.append(((FieldChain)current).getField().getName());
146 | } else if (current.isThroughArrayIndex()) {
147 | sb.append("[").append(((ArrayIndexChain)current).getArrayIndex()).append("]");
148 | }
149 | }
150 | }
151 |
152 | static class FieldChain extends Chain {
153 | private final Field field;
154 |
155 | FieldChain(Chain parent, Field referringField, Object value) {
156 | super(parent, value);
157 | this.field = referringField;
158 | }
159 |
160 | @Override
161 | public boolean isThroughField() {
162 | return true;
163 | }
164 |
165 | @Override
166 | public boolean isThroughArrayIndex() {
167 | return false;
168 | }
169 |
170 | @Override
171 | public Class> getValueType() {
172 | return field.getType();
173 | }
174 |
175 | public Field getField() {
176 | return field;
177 | }
178 | }
179 |
180 | static class ArrayIndexChain extends Chain {
181 | private final int index;
182 |
183 | ArrayIndexChain(Chain parent, int index, Object value) {
184 | super(parent, value);
185 | this.index = index;
186 | }
187 |
188 | @Override
189 | public boolean isThroughField() {
190 | return false;
191 | }
192 |
193 | @Override
194 | public boolean isThroughArrayIndex() {
195 | return true;
196 | }
197 |
198 | @Override
199 | public Class> getValueType() {
200 | return getParent().getValue().getClass().getComponentType();
201 | }
202 |
203 | public int getArrayIndex() {
204 | return index;
205 | }
206 | }
207 | }
208 |
--------------------------------------------------------------------------------
/src/main/java/objectexplorer/InstrumentationGrabber.java:
--------------------------------------------------------------------------------
1 | package objectexplorer;
2 |
3 | import com.google.common.base.Preconditions;
4 | import java.lang.instrument.Instrumentation;
5 |
6 | /**
7 | * Agent call-back that stores the {@link Instrumentation} provided by the JVM.
8 | *
9 | *
Not to be used directly.
10 | */
11 | public class InstrumentationGrabber {
12 | private static volatile Instrumentation instrumentation;
13 |
14 | public static void premain(String agentArgs, Instrumentation inst) {
15 | if (InstrumentationGrabber.instrumentation != null) throw new AssertionError("Already initialized");
16 | InstrumentationGrabber.instrumentation = inst;
17 | }
18 |
19 | private static void checkSetup() {
20 | Preconditions.checkState(instrumentation != null, "Instrumentation is not setup properly. "
21 | + "You have to pass -javaagent:path/to/object-explorer.jar to the java interpreter");
22 | }
23 |
24 | static Instrumentation instrumentation() {
25 | checkSetup();
26 | return instrumentation;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/objectexplorer/MemoryMeasurer.java:
--------------------------------------------------------------------------------
1 | package objectexplorer;
2 |
3 | import com.google.common.base.Preconditions;
4 | import com.google.common.base.Predicate;
5 | import com.google.common.base.Predicates;
6 | import com.google.common.collect.ImmutableList;
7 |
8 | import java.lang.instrument.Instrumentation;
9 |
10 | /**
11 | * A utility that can be used to measure the memory footprint of an arbitrary
12 | * object graph. In a nutshell, the user gives a root object, and this class
13 | * recursively and reflectively explores the object's references.
14 | *
15 | *
This class can only be used if the containing jar has been given to the
16 | * Java VM as an agent, as follows:
17 | * {@code -javaagent:path/to/object-explorer.jar}
18 | *
19 | * @see #measureBytes(Object)
20 | * @see #measureBytes(Object, Predicate)
21 | */
22 | public class MemoryMeasurer {
23 | private static final Instrumentation instrumentation =
24 | InstrumentationGrabber.instrumentation();
25 |
26 | /*
27 | * The bare minimum memory footprint of an enum value, measured empirically.
28 | * This should be subtracted for any enum value encountered, since it
29 | * is static in nature.
30 | */
31 | private static final long costOfBareEnumConstant =
32 | instrumentation.getObjectSize(DummyEnum.CONSTANT);
33 |
34 | private enum DummyEnum {
35 | CONSTANT;
36 | }
37 |
38 | /**
39 | * Measures the memory footprint, in bytes, of an object graph. The object
40 | * graph is defined by a root object and whatever object can be reached
41 | * through that, excluding static fields, {@code Class} objects, and
42 | * fields defined in {@code enum}s (all these are considered shared values,
43 | * which should not contribute to the cost of any single object graph).
44 | *
45 | *
Equivalent to {@code measureBytes(rootObject,
46 | * Predicates.alwaysTrue())}.
47 | *
48 | * @param rootObject the root object that defines the object graph to be
49 | * measured
50 | * @return the memory footprint, in bytes, of the object graph
51 | */
52 | public static long measureBytes(Object rootObject) {
53 | return measureBytes(rootObject, Predicates.alwaysTrue());
54 | }
55 |
56 | /**
57 | * Measures the memory footprint, in bytes, of an object graph. The object
58 | * graph is defined by a root object and whatever object can be reached
59 | * through that, excluding static fields, {@code Class} objects, and
60 | * fields defined in {@code enum}s (all these are considered shared values,
61 | * which should not contribute to the cost of any single object graph), and
62 | * any object for which the user-provided predicate returns {@code false}.
63 | *
64 | * @param rootObject the root object that defines the object graph to be
65 | * measured
66 | * @param objectAcceptor a predicate that returns {@code true} for objects
67 | * to be explored (and treated as part of the object graph), or
68 | * {@code false} to forbid the traversal to traverse the given object
69 | * @return the memory footprint, in bytes, of the object graph
70 | */
71 | public static long measureBytes(Object rootObject, Predicate