├── .gitignore
├── LICENSE
├── README.adoc
├── azure-pipelines.yml
├── findbugs-exclude-filter.xml
├── header.txt
├── pom.xml
└── src
├── hidden
└── resources
│ └── META-INF
│ └── MANIFEST.MF
├── main
└── java
│ └── org
│ └── ehcache
│ └── sizeof
│ ├── Configuration.java
│ ├── Filter.java
│ ├── FilterConfigurator.java
│ ├── FlyWeightType.java
│ ├── ObjectGraphWalker.java
│ ├── SizeOf.java
│ ├── SizeOfFilterSource.java
│ ├── VisitorListener.java
│ ├── annotations
│ ├── AnnotationProxyFactory.java
│ └── IgnoreSizeOf.java
│ ├── filters
│ ├── AnnotationSizeOfFilter.java
│ ├── CombinationSizeOfFilter.java
│ ├── SizeOfFilter.java
│ └── TypeFilter.java
│ ├── impl
│ ├── AgentLoader.java
│ ├── AgentSizeOf.java
│ ├── JvmInformation.java
│ ├── PassThroughFilter.java
│ ├── PrimitiveType.java
│ ├── ReflectionSizeOf.java
│ ├── SizeOfAgent.java
│ └── UnsafeSizeOf.java
│ └── util
│ └── WeakIdentityConcurrentMap.java
└── test
├── java
├── com
│ └── terracotta
│ │ ├── ehcache
│ │ └── special
│ │ │ └── annotation
│ │ │ ├── IgnoreSizeOf.java
│ │ │ ├── IgnoreSizeOffff.java
│ │ │ └── no
│ │ │ └── inherited
│ │ │ └── IgnoreSizeOf.java
│ │ └── special
│ │ └── annotation
│ │ └── IgnoreSizeOf.java
└── org
│ └── ehcache
│ └── sizeof
│ ├── AbstractSizeOfTest.java
│ ├── ConfigurationTest.java
│ ├── CrossCheckingSizeOf.java
│ ├── CrossCheckingSizeOfIT.java
│ ├── FilteredSizeOfTest.java
│ ├── ObjectGraphWalkerTest.java
│ ├── SizeOfFilterSourceTest.java
│ ├── SizeOfTest.java
│ ├── SizeOfTestValues.java
│ ├── annotations
│ ├── AnnotationProxyFactoryTest.java
│ ├── CustomAnnotation.java
│ ├── ExampleEnum.java
│ └── ReferenceAnnotation.java
│ ├── filteredtest
│ ├── AnnotationFilteredPackage.java
│ ├── custom
│ │ ├── CustomAnnotationFilteredPackage.java
│ │ └── package-info.java
│ └── package-info.java
│ ├── filters
│ └── TypeFilterTest.java
│ └── impl
│ ├── AgentLoaderRaceTest.java
│ ├── AgentLoaderSystemPropTest.java
│ ├── AgentLoaderTest.java
│ └── JvmInformationTest.java
└── resources
├── logback-test.xml
└── services
└── org.ehcache.sizeof.FilterConfigurator.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | /.idea/
3 | *.iml
4 |
--------------------------------------------------------------------------------
/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 |
203 |
--------------------------------------------------------------------------------
/README.adoc:
--------------------------------------------------------------------------------
1 | = ehcache-sizeofengine
2 |
3 | == What is this?
4 |
5 | This library lets you size Java Object instances in bytes.
6 |
7 | [source,java,]
8 | ----
9 | SizeOf sizeOf = SizeOf.newInstance(); // <1>
10 | long shallowSize = sizeOf.sizeOf(someObject); // <2>
11 | long deepSize = sizeOf.deepSizeOf(someObject); // <3>
12 | ----
13 | <1> Retrieves one of the multiple engines supported;
14 | <2> Sizes `someObject` instance in bytes;
15 | <3> Sizes `someObject` instance in bytes, as well as all objects it references.
16 |
17 | === The different engines
18 |
19 | When retrieving a new `SizeOf` instance with the static `org.ehcache.sizeof.SizeOf.newInstance`, the library will try to create of
20 | the following engines, in that order:
21 |
22 | . `AgentSizeOf` : Which tries to attach an agent to the JVM process and size objects using `org.ehcache.sizeof.impl.AgentLoader.instrumentation`; otherwise
23 | . `UnsafeSizeOf` : Which will determine Class layouts in memory using `sun.misc.Unsafe`; or finally
24 | . `ReflectionSizeOf` : Which will introspect Class instances and try determining object sizes that way.
25 |
26 | Both the `ReflectionSizeOf` and the `AgentSizeOf` approach were very well covered in Dr. Heinz Kabutz's
27 | http://www.javaspecialists.eu/archive/Issue078.html[Java Specialist Newsletter issue #78] and http://www.javaspecialists.eu/archive/Issue142.html[issue #142] respectively.
28 | Different JVMs and their configurations may affect these sizes, see blob/master/src/main/java/org/ehcache/sizeof/impl/JvmInformation.java[JvmInformation enum] for more details.
29 | Finally, the `UnsafeSizeOf`, we've discovered and, as far as we know, were the first ones to use.
30 |
31 | == Avoiding sizing certain types
32 |
33 | In some cases you want to control how far the deep sizing goes. You can do this using the `org.ehcache.sizeof.Filter` SPI.
34 |
35 | === Using existing filter configurators
36 |
37 | You simply need to add the jars of the modules that you want along side this project's jar
38 |
39 | * Hibernate
40 | ** https://github.com/alexsnaps/ehcache-sizeofengine-hibernate[https://github.com/alexsnaps/ehcache-sizeofengine-hibernate v4+]
41 | ** https://github.com/alexsnaps/ehcache-sizeofengine-hibernate3[https://github.com/alexsnaps/ehcache-sizeofengine-hibernate3 v3+]
42 | * Groovy
43 | ** https://github.com/noamt/ehcache-sizeofengine-groovy
44 |
45 | == Configuring the Filter yourself
46 |
47 | In order to ignore fields or instances of certain classes when sizing object graphs, you'll have to
48 |
49 | . Create a http://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html[ServiceLoader] project, for http://terracotta-oss.github.io/ehcache-sizeofengine/apidocs/net/sf/ehcache/sizeofengine/FilterConfigurator.html[net.sf.ehcache.sizeofengine.FilterConfigurator]
50 | * Have your jar contain a text file named +META-INF/services/org.ehcache.sizeof.FilterConfigurator+
51 | * The file should contain the fully qualified class name of your implementation
52 | . Implement http://terracotta-oss.github.io/ehcache-sizeofengine/apidocs/net/sf/ehcache/sizeofengine/FilterConfigurator.html[FilterConfigurator]'s configure method to configure the http://terracotta-oss.github.io/ehcache-sizeofengine/apidocs/net/sf/ehcache/sizeofengine/Filter.html[Filter] of classes and fields
53 | . put your jar on your application's classpath, along side of the ehcache jar and this ehcache-sizeofengine jar
54 | . Use Ehcache's http://ehcache.org/documentation/arc[Automatic Resource Control] for your heap tier
55 |
56 | == Example
57 |
58 | [source,java]
59 | ----
60 | public static final class StupidConfigurator implements FilterConfigurator {
61 |
62 | @Override
63 | public void configure(final Filter ehcacheFilter) {
64 | // Will not size any instance of Number, and given the second arg, no subtype neither
65 | ehcacheFilter.ignoreInstancesOf(Number.class, false);
66 | }
67 | }
68 | ----
69 |
70 | There can be as many +FilterConfigurator+ on the classpath as required, they'll have configure the filter once.
71 | The +Filter+ is shared across all +SizeOfEngine+ instances created.
72 |
73 | == Using it
74 |
75 | === Maven
76 |
77 | Releases are available from Maven Central.
78 |
79 | [source,xml]
80 | ----
81 |
82 | org.ehcache
83 | sizeof
84 | ${sizeof.version}
85 |
86 | ----
87 |
88 | Snapshots are available from the https://oss.sonatype.org/content/repositories/snapshots[Sonatype OSS snapshots] repository.
89 | In order to access the snapshots, you need to add the following repository to your +pom.xml+:
90 |
91 | [source,xml]
92 | ----
93 |
94 | sonatype-nexus-snapshots
95 | Sonatype Nexus Snapshots
96 | https://oss.sonatype.org/content/repositories/snapshots
97 |
98 | false
99 |
100 |
101 | true
102 |
103 |
104 | ----
105 |
--------------------------------------------------------------------------------
/azure-pipelines.yml:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright Terracotta, Inc.
3 | # Copyright IBM Corp. 2024, 2025
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 | #
17 |
18 | # See shared code location for steps and parameters:
19 | # https://dev.azure.com/TerracottaCI/_git/terracotta
20 |
21 | resources:
22 | repositories:
23 | - repository: templates
24 | type: git
25 | name: terracotta/terracotta
26 |
27 | jobs:
28 | - template: build-templates/maven-common.yml@templates
29 |
--------------------------------------------------------------------------------
/findbugs-exclude-filter.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/header.txt:
--------------------------------------------------------------------------------
1 | Copyright Terracotta, Inc.
2 | Copyright IBM Corp. 2024, 2025
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 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | org.sonatype.oss
9 | oss-parent
10 | 9
11 |
12 |
13 | org.ehcache
14 | sizeof
15 | 0.4.3-SNAPSHOT
16 | jar
17 |
18 | Ehcache SizeOf Engine
19 | SizeOf engine, extracted from Ehcache
20 |
21 | https://github.com/ehcache/sizeof
22 |
23 |
24 | Terracotta
25 | http://terracotta.org
26 |
27 |
28 |
29 |
30 | The Apache Software License, Version 2.0
31 | http://www.apache.org/licenses/LICENSE-2.0.txt
32 | repo
33 |
34 |
35 |
36 |
37 | UTF-8
38 | UTF-8
39 | 1.8
40 | 1.7.32
41 | [${slf4j.base.version},1.7.9999)
42 | 1.2.11
43 | [${logback.base.version},1.2.9999)
44 |
45 |
46 |
47 |
48 | org.slf4j
49 | slf4j-api
50 | ${slf4j.range.version}
51 |
52 |
53 | ch.qos.logback
54 | logback-classic
55 | ${logback.range.version}
56 | test
57 |
58 |
59 | junit
60 | junit
61 | 4.12
62 | test
63 |
64 |
65 | org.hamcrest
66 | hamcrest-library
67 | 1.3
68 | test
69 |
70 |
71 | org.ow2.asm
72 | asm
73 | 6.0
74 | test
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | maven-clean-plugin
83 | 3.0.0
84 |
85 |
86 | maven-compiler-plugin
87 | 3.8.1
88 |
89 |
90 | maven-deploy-plugin
91 | 3.0.0-M1
92 |
93 |
94 | maven-enforcer-plugin
95 | 3.0.0-M1
96 |
97 |
98 | maven-failsafe-plugin
99 | 3.0.0-M5
100 |
101 |
102 | maven-gpg-plugin
103 | 1.6
104 |
105 |
106 | maven-install-plugin
107 | 3.0.0-M1
108 |
109 |
110 | maven-jar-plugin
111 | 3.1.2
112 |
113 |
114 | maven-javadoc-plugin
115 | 3.1.1
116 |
117 | ${skipJavadoc}
118 | 8
119 |
120 |
121 |
122 | maven-resources-plugin
123 | 3.0.1
124 |
125 |
126 | maven-site-plugin
127 | 3.5.1
128 |
129 |
130 | maven-source-plugin
131 | 3.1.0
132 |
133 |
134 | maven-surefire-plugin
135 | 3.0.0-M5
136 |
137 |
138 |
139 | org.codehaus.mojo
140 | findbugs-maven-plugin
141 | 3.0.5
142 |
143 |
144 | org.jacoco
145 | jacoco-maven-plugin
146 | 0.8.3
147 |
148 |
149 | org.apache.rat
150 | apache-rat-plugin
151 | 0.12
152 |
153 |
154 | org.codehaus.gmavenplus
155 | gmavenplus-plugin
156 | 1.6
157 |
158 |
159 | org.codehaus.mojo
160 | versions-maven-plugin
161 | 2.5
162 |
163 |
164 | com.mycila.maven-license-plugin
165 | maven-license-plugin
166 | 1.10.b1
167 |
168 | true
169 | ${project.basedir}/header.txt
170 |
171 |
172 | src/**/*.java
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 | maven-compiler-plugin
182 |
183 | ${java.version}
184 | ${java.version}
185 |
186 |
187 |
188 | org.apache.rat
189 | apache-rat-plugin
190 |
191 |
192 | verify
193 |
194 | check
195 |
196 |
197 |
198 | src/hidden/**
199 | README.adoc
200 | **/*.txt
201 |
202 |
203 |
204 |
205 |
206 |
207 | org.codehaus.gmavenplus
208 | gmavenplus-plugin
209 |
210 |
211 | create-agent-jar
212 | process-classes
213 |
214 | execute
215 |
216 |
217 |
218 |
230 |
231 |
232 |
233 |
234 |
235 |
236 | org.codehaus.groovy
237 | groovy-all
238 |
239 | 2.4.12
240 | runtime
241 |
242 |
243 |
244 |
245 | maven-surefire-plugin
246 |
247 | -Xms64m -Xmx64m
248 | false
249 |
250 |
251 |
252 | maven-failsafe-plugin
253 |
254 |
255 |
256 | integration-test
257 | verify
258 |
259 |
260 |
261 |
262 |
263 | org.jacoco
264 | jacoco-maven-plugin
265 |
266 |
267 | prepare-agent
268 |
269 | prepare-agent
270 |
271 |
272 | jacocoArgs
273 |
274 |
275 |
276 | report
277 | verify
278 |
279 | report
280 |
281 |
282 |
283 |
284 |
285 | org.codehaus.mojo
286 | findbugs-maven-plugin
287 |
288 | ${basedir}/findbugs-exclude-filter.xml
289 |
290 |
291 |
292 |
293 | check
294 |
295 |
296 |
297 |
298 |
299 | maven-enforcer-plugin
300 |
301 |
302 |
303 | [3.0.5,)
304 |
305 |
306 |
307 |
308 |
309 | enforce-maven
310 |
311 | enforce
312 |
313 |
314 |
315 |
316 |
317 | maven-source-plugin
318 |
319 |
320 | attach-sources
321 | verify
322 |
323 | jar-no-fork
324 |
325 |
326 |
327 |
328 |
329 | maven-javadoc-plugin
330 |
331 |
332 | attach-javadocs
333 |
334 | jar
335 |
336 |
337 | ${skipJavadoc}
338 | 8
339 | -Xdoclint:none
340 |
341 |
342 |
343 |
344 |
345 | org.sonatype.plugins
346 | nexus-staging-maven-plugin
347 | 1.6.13
348 | true
349 |
350 |
351 | https://oss.sonatype.org
352 |
353 | sonatype-nexus-staging
354 | ${maven.deploy.skip}
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 | deploy-sonatype
363 |
364 |
365 | sonatype-nexus-staging
366 | http://oss.sonatype.org/service/local/staging/deploy/maven2
367 |
368 |
369 |
370 |
371 |
372 | sign-artifacts
373 |
374 |
375 | performRelease
376 | true
377 |
378 |
379 |
380 |
381 |
382 | maven-gpg-plugin
383 |
384 |
385 | sign-artifacts
386 | verify
387 |
388 | sign
389 |
390 |
391 | Terracotta Release Engineer
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 | https://github.com/ehcache/sizeof
403 | scm:git:https://github.com/ehcache/sizeof.git
404 | scm:git:git@github.com:ehcache/sizeof.git
405 |
406 |
407 |
408 |
--------------------------------------------------------------------------------
/src/hidden/resources/META-INF/MANIFEST.MF:
--------------------------------------------------------------------------------
1 | Manifest-Version: 1.0
2 | Agent-Class: org.ehcache.sizeof.impl.SizeOfAgent
3 | Premain-Class: org.ehcache.sizeof.impl.SizeOfAgent
4 |
--------------------------------------------------------------------------------
/src/main/java/org/ehcache/sizeof/Configuration.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof;
18 |
19 | import org.ehcache.sizeof.filters.SizeOfFilter;
20 |
21 | import java.util.ArrayList;
22 | import java.util.Arrays;
23 | import java.util.Collections;
24 | import java.util.List;
25 |
26 | /**
27 | * @author Alex Snaps
28 | */
29 | public final class Configuration {
30 | private final int maxDepth;
31 | private final boolean abort;
32 | private final boolean silent;
33 | private final SizeOfFilter[] filters;
34 |
35 | public Configuration(final int maxDepth, final boolean abort, final boolean silent, final SizeOfFilter... filters) {
36 | this.maxDepth = maxDepth;
37 | this.abort = abort;
38 | this.silent = silent;
39 | this.filters = filters;
40 | }
41 |
42 | public int getMaxDepth() {
43 | return maxDepth;
44 | }
45 |
46 | public boolean isAbort() {
47 | return abort;
48 | }
49 |
50 | public boolean isSilent() {
51 | return silent;
52 | }
53 |
54 | public SizeOfFilter[] getFilters() {
55 | return filters;
56 | }
57 |
58 | public static final class Builder {
59 |
60 | private int maxDepth;
61 | private boolean silent;
62 | private boolean abort;
63 | private final List filters = new ArrayList<>();
64 |
65 | public Builder() {
66 | }
67 |
68 | public Builder(Configuration cfg) {
69 | maxDepth(cfg.maxDepth);
70 | silent(cfg.silent);
71 | abort(cfg.abort);
72 | Collections.addAll(filters, cfg.filters);
73 | }
74 |
75 | public Builder maxDepth(int maxDepth) {
76 | this.maxDepth = maxDepth;
77 | return this;
78 | }
79 |
80 | public Builder silent(boolean silent) {
81 | this.silent = silent;
82 | return this;
83 | }
84 |
85 | public Builder abort(boolean abort) {
86 | this.abort = abort;
87 | return this;
88 | }
89 |
90 | public Builder addFilter(SizeOfFilter filter) {
91 | if (!filters.contains(filter)) {
92 | filters.add(filter);
93 | }
94 | return this;
95 | }
96 |
97 | public Builder addFilters(SizeOfFilter... filters) {
98 | for (SizeOfFilter filter : filters) {
99 | addFilter(filter);
100 | }
101 | return this;
102 | }
103 |
104 | public Builder removeFilter(SizeOfFilter filter) {
105 | filters.remove(filter);
106 | return this;
107 | }
108 |
109 | public Builder removeFilters(SizeOfFilter... filters) {
110 | this.filters.removeAll(Arrays.asList(filters));
111 | return this;
112 | }
113 |
114 | public Builder clearlFilters() {
115 | this.filters.clear();
116 | return this;
117 | }
118 |
119 | public Configuration build() {
120 | return new Configuration(maxDepth, abort, silent, filters.toArray(new SizeOfFilter[filters.size()]));
121 | }
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/src/main/java/org/ehcache/sizeof/Filter.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof;
18 |
19 | import java.lang.reflect.Field;
20 |
21 | /**
22 | * Filters all the sizing operation performed by a SizeOfEngine instance
23 | *
24 | * @author Alex Snaps
25 | */
26 | public interface Filter {
27 |
28 | /**
29 | * Adds the class to the ignore list. Can be strict, or include subtypes
30 | *
31 | * @param clazz the class to ignore
32 | * @param strict true if to be ignored strictly, or false to include sub-classes
33 | */
34 | void ignoreInstancesOf(final Class clazz, final boolean strict);
35 |
36 | /**
37 | * Adds a field to the ignore list. When that field is walked to by the SizeOfEngine, it won't navigate the graph further
38 | *
39 | * @param field the field to stop navigating the graph at
40 | */
41 | void ignoreField(final Field field);
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/org/ehcache/sizeof/FilterConfigurator.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof;
18 |
19 | /**
20 | * A configurator, instantiated by the {@link java.util.ServiceLoader}, that will get to configure the {@link Filter}
21 | *
22 | * @author Alex Snaps
23 | */
24 | public interface FilterConfigurator {
25 |
26 | /**
27 | * Invoked by the framework to let this instance configure the filter
28 | * @param filter the filter managed by the framework
29 | */
30 | void configure(Filter filter);
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/org/ehcache/sizeof/FlyWeightType.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof;
18 |
19 | import java.lang.reflect.Field;
20 | import java.lang.reflect.Modifier;
21 | import java.math.BigDecimal;
22 | import java.math.BigInteger;
23 | import java.math.MathContext;
24 | import java.net.Proxy;
25 | import java.nio.charset.CodingErrorAction;
26 | import java.util.Collections;
27 | import java.util.HashMap;
28 | import java.util.IdentityHashMap;
29 | import java.util.Locale;
30 | import java.util.Map;
31 | import java.util.Set;
32 | import java.util.logging.Logger;
33 |
34 | import javax.xml.datatype.DatatypeConstants;
35 | import javax.xml.namespace.QName;
36 |
37 | /**
38 | * Enum with all the flyweight types that we check for sizeOf measurements
39 | *
40 | * @author Alex Snaps
41 | */
42 | @SuppressWarnings("BoxingBoxedValue")
43 | // Don't be smart IDE, these _ARE_ required, we want access to the instances in the cache.
44 | enum FlyweightType {
45 |
46 | /**
47 | * java.lang.Enum
48 | */
49 | ENUM(Enum.class) {
50 | @Override
51 | boolean isShared(final Object obj) { return true; }
52 | },
53 | /**
54 | * java.lang.Class
55 | */
56 | CLASS(Class.class) {
57 | @Override
58 | boolean isShared(final Object obj) { return true; }
59 | },
60 | // XXX There is no nullipotent way of determining the interned status of a string
61 | // There are numerous String constants within the JDK (see list at http://docs.oracle.com/javase/7/docs/api/constant-values.html),
62 | // but enumerating all of them would lead to lots of == tests.
63 | //STRING(String.class) {
64 | // @Override
65 | // boolean isShared(final Object obj) { return obj == ((String)obj).intern(); }
66 | //},
67 | /**
68 | * java.lang.Boolean
69 | */
70 | BOOLEAN(Boolean.class) {
71 | @Override
72 | boolean isShared(final Object obj) { return obj == Boolean.TRUE || obj == Boolean.FALSE; }
73 | },
74 | /**
75 | * java.lang.Integer
76 | */
77 | INTEGER(Integer.class) {
78 | @Override
79 | boolean isShared(final Object obj) {
80 | int value = (Integer)obj;
81 | return value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE && obj == Integer.valueOf(value);
82 | }
83 | },
84 | /**
85 | * java.lang.Short
86 | */
87 | SHORT(Short.class) {
88 | @Override
89 | boolean isShared(final Object obj) {
90 | short value = (Short)obj;
91 | return value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE && obj == Short.valueOf(value);
92 | }
93 | },
94 | /**
95 | * java.lang.Byte
96 | */
97 | BYTE(Byte.class) {
98 | @Override
99 | boolean isShared(final Object obj) { return obj == Byte.valueOf((Byte)obj); }
100 | },
101 | /**
102 | * java.lang.Long
103 | */
104 | LONG(Long.class) {
105 | @Override
106 | boolean isShared(final Object obj) {
107 | long value = (Long)obj;
108 | return value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE && obj == Long.valueOf(value);
109 | }
110 | },
111 | /**
112 | * java.math.BigInteger
113 | */
114 | BIGINTEGER(BigInteger.class) {
115 | @Override
116 | boolean isShared(final Object obj) {
117 | return obj == BigInteger.ZERO || obj == BigInteger.ONE || obj == BigInteger.TEN;
118 | }
119 | },
120 | /**
121 | * java.math.BigDecimal
122 | */
123 | BIGDECIMAL(BigDecimal.class) {
124 | @Override
125 | boolean isShared(final Object obj) {
126 | return obj == BigDecimal.ZERO || obj == BigDecimal.ONE || obj == BigDecimal.TEN;
127 | }
128 | },
129 | /**
130 | * java.math.MathContext
131 | */
132 | MATHCONTEXT(MathContext.class) {
133 | @Override
134 | boolean isShared(final Object obj) {
135 | return obj == MathContext.UNLIMITED || obj == MathContext.DECIMAL32 || obj == MathContext.DECIMAL64 || obj == MathContext.DECIMAL128;
136 | }
137 | },
138 | /**
139 | * java.lang.Character
140 | */
141 | CHARACTER(Character.class) {
142 | @Override
143 | boolean isShared(final Object obj) { return (Character)obj <= Byte.MAX_VALUE && obj == Character.valueOf((Character)obj); }
144 | },
145 | /**
146 | * java.lang.Locale
147 | */
148 | LOCALE(Locale.class) {
149 | @Override
150 | boolean isShared(final Object obj) {
151 | return obj instanceof Locale && GLOBAL_LOCALES.contains(obj);
152 | }
153 | },
154 | /**
155 | * java.util.Logger
156 | */
157 | LOGGER(Logger.class) {
158 | @Override
159 | @SuppressWarnings("deprecation")
160 | boolean isShared(final Object obj) { return obj == Logger.global; }
161 | },
162 | /**
163 | * java.net.Proxy
164 | */
165 | PROXY(Proxy.class) {
166 | @Override
167 | boolean isShared(final Object obj) { return obj == Proxy.NO_PROXY; }
168 | },
169 | /**
170 | * java.nio.charset.CodingErrorAction
171 | */
172 | CODINGERRORACTION(CodingErrorAction.class) {
173 | @Override
174 | boolean isShared(final Object obj) { return true; }
175 | },
176 | /**
177 | * javax.xml.datatype.DatatypeConstants.Field
178 | */
179 | DATATYPECONSTANTS_FIELD(DatatypeConstants.Field.class) {
180 | @Override
181 | boolean isShared(final Object obj) { return true; }
182 | },
183 | /**
184 | * javax.xml.namespace.QName
185 | */
186 | QNAME(QName.class) {
187 | @Override
188 | boolean isShared(final Object obj) {
189 | return obj == DatatypeConstants.DATETIME
190 | || obj == DatatypeConstants.TIME
191 | || obj == DatatypeConstants.DATE
192 | || obj == DatatypeConstants.GYEARMONTH
193 | || obj == DatatypeConstants.GMONTHDAY
194 | || obj == DatatypeConstants.GYEAR
195 | || obj == DatatypeConstants.GMONTH
196 | || obj == DatatypeConstants.GDAY
197 | || obj == DatatypeConstants.DURATION
198 | || obj == DatatypeConstants.DURATION_DAYTIME
199 | || obj == DatatypeConstants.DURATION_YEARMONTH;
200 | }
201 | },
202 | /**
203 | * misc comparisons that can not rely on the object's class.
204 | */
205 | MISC(Void.class) {
206 | @Override
207 | boolean isShared(final Object obj) {
208 | boolean emptyCollection = obj == Collections.EMPTY_SET || obj == Collections.EMPTY_LIST || obj == Collections.EMPTY_MAP;
209 | boolean systemStream = obj == System.in || obj == System.out || obj == System.err;
210 | return emptyCollection || systemStream || obj == String.CASE_INSENSITIVE_ORDER;
211 | }
212 | };
213 |
214 | private static final Map, FlyweightType> TYPE_MAPPINGS = new HashMap<>();
215 |
216 | static {
217 | for (FlyweightType type : FlyweightType.values()) {
218 | TYPE_MAPPINGS.put(type.clazz, type);
219 | }
220 | }
221 |
222 | private static final Set GLOBAL_LOCALES;
223 |
224 | static {
225 | Map locales = new IdentityHashMap<>();
226 | for (Field f : Locale.class.getFields()) {
227 | int modifiers = f.getModifiers();
228 | if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers) && Locale.class.equals(f.getType())) {
229 | try {
230 | locales.put((Locale)f.get(null), null);
231 | } catch (IllegalArgumentException | IllegalAccessException e) {
232 | //
233 | }
234 | }
235 | }
236 | GLOBAL_LOCALES = locales.keySet();
237 | }
238 |
239 | private final Class> clazz;
240 |
241 | FlyweightType(final Class> clazz) {
242 | this.clazz = clazz;
243 | }
244 |
245 | /**
246 | * Whether this is a shared object
247 | *
248 | * @param obj the object to check for
249 | * @return true, if shared
250 | */
251 | abstract boolean isShared(Object obj);
252 |
253 | /**
254 | * Will return the Flyweight enum instance for the flyweight Class, or null if type isn't flyweight
255 | *
256 | * @param aClazz the class we need the FlyweightType instance for
257 | * @return the FlyweightType, or null
258 | */
259 | static FlyweightType getFlyweightType(final Class> aClazz) {
260 | if (aClazz.isEnum() || (aClazz.getSuperclass() != null && aClazz.getSuperclass().isEnum())) {
261 | return ENUM;
262 | } else {
263 | FlyweightType flyweightType = TYPE_MAPPINGS.get(aClazz);
264 | return flyweightType != null ? flyweightType : MISC;
265 | }
266 | }
267 | }
268 |
--------------------------------------------------------------------------------
/src/main/java/org/ehcache/sizeof/ObjectGraphWalker.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof;
18 |
19 | import org.ehcache.sizeof.filters.SizeOfFilter;
20 | import org.ehcache.sizeof.util.WeakIdentityConcurrentMap;
21 | import org.slf4j.Logger;
22 | import org.slf4j.LoggerFactory;
23 |
24 | import java.lang.ref.SoftReference;
25 | import java.lang.reflect.Array;
26 | import java.lang.reflect.Field;
27 | import java.lang.reflect.Modifier;
28 | import java.util.ArrayDeque;
29 | import java.util.ArrayList;
30 | import java.util.Collection;
31 | import java.util.Deque;
32 | import java.util.IdentityHashMap;
33 | import java.util.Set;
34 |
35 | import static java.util.Collections.newSetFromMap;
36 |
37 | /**
38 | * This will walk an object graph and let you execute some "function" along the way
39 | *
40 | * @author Alex Snaps
41 | */
42 | final class ObjectGraphWalker {
43 |
44 | private static final Logger LOG = LoggerFactory.getLogger(ObjectGraphWalker.class);
45 | private static final String VERBOSE_DEBUG_LOGGING = "org.ehcache.sizeof.verboseDebugLogging";
46 | private static final boolean USE_VERBOSE_DEBUG_LOGGING;
47 |
48 | private final WeakIdentityConcurrentMap, SoftReference>> fieldCache =
49 | new WeakIdentityConcurrentMap<>();
50 | private final WeakIdentityConcurrentMap, Boolean> classCache =
51 | new WeakIdentityConcurrentMap<>();
52 |
53 | private final boolean bypassFlyweight;
54 | private final SizeOfFilter sizeOfFilter;
55 |
56 | private final Visitor visitor;
57 |
58 | static {
59 | USE_VERBOSE_DEBUG_LOGGING = getVerboseSizeOfDebugLogging();
60 | }
61 |
62 | /**
63 | * Constructor
64 | *
65 | * @param visitor the visitor to use
66 | * @param filter the filtering
67 | * @param bypassFlyweight the filtering
68 | * @see Visitor
69 | * @see SizeOfFilter
70 | */
71 | ObjectGraphWalker(Visitor visitor, SizeOfFilter filter, final boolean bypassFlyweight) {
72 | if(visitor == null) {
73 | throw new NullPointerException("Visitor can't be null");
74 | }
75 | if(filter == null) {
76 | throw new NullPointerException("SizeOfFilter can't be null");
77 | }
78 | this.visitor = visitor;
79 | this.sizeOfFilter = filter;
80 | this.bypassFlyweight = bypassFlyweight;
81 | }
82 |
83 | private static boolean getVerboseSizeOfDebugLogging() {
84 |
85 | String verboseString = System.getProperty(VERBOSE_DEBUG_LOGGING, "false").toLowerCase();
86 |
87 | return verboseString.equals("true");
88 | }
89 |
90 | /**
91 | * The visitor to execute the function on each node of the graph
92 | * This is only to be used for the sizing of an object graph in memory!
93 | */
94 | interface Visitor {
95 | /**
96 | * The visit method executed on each node
97 | *
98 | * @param object the reference at that node
99 | * @return a long for you to do things with...
100 | */
101 | long visit(Object object);
102 | }
103 |
104 | /**
105 | * Walk the graph and call into the "visitor"
106 | *
107 | * @param root the roots of the objects (a shared graph will only be visited once)
108 | * @return the sum of all Visitor#visit returned values
109 | */
110 | long walk(Object... root) {
111 | return walk(null, root);
112 | }
113 |
114 | /**
115 | * Walk the graph and call into the "visitor"
116 | *
117 | * @param visitorListener A decorator for the Visitor
118 | * @param root the roots of the objects (a shared graph will only be visited once)
119 | * @return the sum of all Visitor#visit returned values
120 | */
121 | long walk(VisitorListener visitorListener, Object... root) {
122 | final StringBuilder traversalDebugMessage;
123 | if (USE_VERBOSE_DEBUG_LOGGING && LOG.isDebugEnabled()) {
124 | traversalDebugMessage = new StringBuilder();
125 | } else {
126 | traversalDebugMessage = null;
127 | }
128 | long result = 0;
129 | Deque toVisit = new ArrayDeque<>();
130 | Set visited = newSetFromMap(new IdentityHashMap<>());
131 |
132 | if (root != null) {
133 | if (traversalDebugMessage != null) {
134 | traversalDebugMessage.append("visiting ");
135 | }
136 | for (Object object : root) {
137 | nullSafeAdd(toVisit, object);
138 | if (traversalDebugMessage != null && object != null) {
139 | traversalDebugMessage.append(object.getClass().getName())
140 | .append("@").append(System.identityHashCode(object)).append(", ");
141 | }
142 | }
143 | if (traversalDebugMessage != null) {
144 | traversalDebugMessage.deleteCharAt(traversalDebugMessage.length() - 2).append("\n");
145 | }
146 | }
147 |
148 | while (!toVisit.isEmpty()) {
149 |
150 | Object ref = toVisit.pop();
151 |
152 | if (visited.add(ref)) {
153 | Class> refClass = ref.getClass();
154 | if (!byPassIfFlyweight(ref) && shouldWalkClass(refClass)) {
155 | if (refClass.isArray() && !refClass.getComponentType().isPrimitive()) {
156 | for (int i = 0; i < Array.getLength(ref); i++) {
157 | nullSafeAdd(toVisit, Array.get(ref, i));
158 | }
159 | } else {
160 | for (Field field : getFilteredFields(refClass)) {
161 | try {
162 | nullSafeAdd(toVisit, field.get(ref));
163 | } catch (IllegalAccessException ex) {
164 | throw new RuntimeException(ex);
165 | }
166 | }
167 | }
168 |
169 | final long visitSize = visitor.visit(ref);
170 | if (visitorListener != null) {
171 | visitorListener.visited(ref, visitSize);
172 | }
173 | if (traversalDebugMessage != null) {
174 | traversalDebugMessage.append(" ").append(visitSize).append("b\t\t")
175 | .append(ref.getClass().getName()).append("@").append(System.identityHashCode(ref)).append("\n");
176 | }
177 | result += visitSize;
178 | } else if (traversalDebugMessage != null) {
179 | traversalDebugMessage.append(" ignored\t")
180 | .append(ref.getClass().getName()).append("@").append(System.identityHashCode(ref)).append("\n");
181 | }
182 | }
183 | }
184 |
185 | if (traversalDebugMessage != null) {
186 | traversalDebugMessage.append("Total size: ").append(result).append(" bytes\n");
187 | LOG.debug(traversalDebugMessage.toString());
188 | }
189 | return result;
190 | }
191 |
192 | /**
193 | * Returns the filtered fields for a particular type
194 | *
195 | * @param refClass the type
196 | * @return A collection of fields to be visited
197 | */
198 | private Collection getFilteredFields(Class> refClass) {
199 | SoftReference> ref = fieldCache.get(refClass);
200 | Collection fieldList = ref != null ? ref.get() : null;
201 | if (fieldList != null) {
202 | return fieldList;
203 | } else {
204 | Collection result;
205 | result = sizeOfFilter.filterFields(refClass, getAllFields(refClass));
206 | if (USE_VERBOSE_DEBUG_LOGGING && LOG.isDebugEnabled()) {
207 | for (Field field : result) {
208 | if (Modifier.isTransient(field.getModifiers())) {
209 | LOG.debug("SizeOf engine walking transient field '{}' of class {}", field.getName(), refClass.getName());
210 | }
211 | }
212 | }
213 | fieldCache.put(refClass, new SoftReference<>(result));
214 | return result;
215 | }
216 | }
217 |
218 | private boolean shouldWalkClass(Class> refClass) {
219 | Boolean cached = classCache.get(refClass);
220 | if (cached == null) {
221 | cached = sizeOfFilter.filterClass(refClass);
222 | classCache.put(refClass, cached);
223 | }
224 | return cached;
225 | }
226 |
227 | private static void nullSafeAdd(final Deque toVisit, final Object o) {
228 | if (o != null) {
229 | toVisit.push(o);
230 | }
231 | }
232 |
233 | /**
234 | * Returns all non-primitive fields for the entire class hierarchy of a type
235 | *
236 | * @param refClass the type
237 | * @return all fields for that type
238 | */
239 | private static Collection getAllFields(Class> refClass) {
240 | Collection fields = new ArrayList<>();
241 | for (Class> klazz = refClass; klazz != null; klazz = klazz.getSuperclass()) {
242 | for (Field field : klazz.getDeclaredFields()) {
243 | if (!Modifier.isStatic(field.getModifiers()) &&
244 | !field.getType().isPrimitive()) {
245 | try {
246 | field.setAccessible(true);
247 | } catch (SecurityException e) {
248 | LOG.error("Security settings prevent Ehcache from accessing the subgraph beneath '{}'" +
249 | " - cache sizes may be underestimated as a result", field, e);
250 | continue;
251 | } catch (RuntimeException e) {
252 | LOG.warn("The JVM is preventing Ehcache from accessing the subgraph beneath '{}'" +
253 | " - cache sizes may be underestimated as a result", field, e);
254 | continue;
255 | }
256 | fields.add(field);
257 | }
258 | }
259 | }
260 | return fields;
261 | }
262 |
263 | private boolean byPassIfFlyweight(Object obj) {
264 | if(bypassFlyweight) {
265 | FlyweightType type = FlyweightType.getFlyweightType(obj.getClass());
266 | return type != null && type.isShared(obj);
267 | }
268 | return false;
269 | }
270 |
271 | }
272 |
--------------------------------------------------------------------------------
/src/main/java/org/ehcache/sizeof/SizeOf.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof;
18 |
19 | import org.ehcache.sizeof.filters.CombinationSizeOfFilter;
20 | import org.ehcache.sizeof.filters.SizeOfFilter;
21 | import org.ehcache.sizeof.impl.AgentSizeOf;
22 | import org.ehcache.sizeof.impl.ReflectionSizeOf;
23 | import org.ehcache.sizeof.impl.UnsafeSizeOf;
24 | import org.ehcache.sizeof.util.WeakIdentityConcurrentMap;
25 |
26 | /**
27 | * Abstract sizeOf for Java. It will rely on a proper sizeOf to measure sizes of entire object graphs
28 | *
29 | * @author Alex Snaps
30 | */
31 | public abstract class SizeOf {
32 |
33 | private final ObjectGraphWalker walker;
34 |
35 | /**
36 | * Builds a new SizeOf that will filter fields according to the provided filter
37 | *
38 | * @param fieldFilter The filter to apply
39 | * @param caching whether to cache reflected fields
40 | * @param bypassFlyweight whether "Flyweight Objects" are to be ignored
41 | * @see org.ehcache.sizeof.filters.SizeOfFilter
42 | */
43 | public SizeOf(SizeOfFilter fieldFilter, boolean caching, boolean bypassFlyweight) {
44 | ObjectGraphWalker.Visitor visitor;
45 | if (caching) {
46 | visitor = new CachingSizeOfVisitor();
47 | } else {
48 | visitor = new SizeOfVisitor();
49 | }
50 | this.walker = new ObjectGraphWalker(visitor, fieldFilter, bypassFlyweight);
51 | }
52 |
53 | /**
54 | * Calculates the size in memory (heap) of the instance passed in, not navigating the down graph
55 | *
56 | * @param obj the object to measure the size of
57 | * @return the object size in memory in bytes
58 | */
59 | public abstract long sizeOf(Object obj);
60 |
61 | /**
62 | * Measures the size in memory (heap) of the objects passed in, walking their graph down
63 | * Any overlap of the graphs being passed in will be recognized and only measured once
64 | *
65 | * @param listener A listener to visited objects
66 | * @param obj the root objects of the graphs to measure
67 | * @return the total size in bytes for these objects
68 | * @see #sizeOf(Object)
69 | */
70 | public long deepSizeOf(VisitorListener listener, Object... obj) {
71 | return walker.walk(listener, obj);
72 | }
73 |
74 | public long deepSizeOf(Object... obj) {
75 | return walker.walk(null, obj);
76 | }
77 |
78 | public static SizeOf newInstance(final SizeOfFilter... filters) {
79 | return newInstance(true, true, filters);
80 | }
81 |
82 | public static SizeOf newInstance(boolean bypassFlyweight, boolean cache, final SizeOfFilter... filters) {
83 | final SizeOfFilter filter = new CombinationSizeOfFilter(filters);
84 | try {
85 | return new AgentSizeOf(filter, cache, bypassFlyweight);
86 | } catch (UnsupportedOperationException e) {
87 | try {
88 | return new UnsafeSizeOf(filter, cache, bypassFlyweight);
89 | } catch (UnsupportedOperationException f) {
90 | try {
91 | return new ReflectionSizeOf(filter, cache, bypassFlyweight);
92 | } catch (UnsupportedOperationException g) {
93 | throw new UnsupportedOperationException("A suitable SizeOf engine could not be loaded: " + e + ", " + f + ", " + g);
94 | }
95 | }
96 | }
97 | }
98 |
99 | /**
100 | * Will return the sizeOf each instance
101 | */
102 | private class SizeOfVisitor implements ObjectGraphWalker.Visitor {
103 |
104 | /**
105 | * {@inheritDoc}
106 | */
107 | public long visit(Object object) {
108 | return sizeOf(object);
109 | }
110 | }
111 |
112 | /**
113 | * Will Cache already visited types
114 | */
115 | private class CachingSizeOfVisitor implements ObjectGraphWalker.Visitor {
116 | private final WeakIdentityConcurrentMap, Long> cache = new WeakIdentityConcurrentMap<>();
117 |
118 | /**
119 | * {@inheritDoc}
120 | */
121 | public long visit(final Object object) {
122 | Class> klazz = object.getClass();
123 | Long cachedSize = cache.get(klazz);
124 | if (cachedSize == null) {
125 | if (klazz.isArray()) {
126 | return sizeOf(object);
127 | } else {
128 | long size = sizeOf(object);
129 | cache.put(klazz, size);
130 | return size;
131 | }
132 | } else {
133 | return cachedSize;
134 | }
135 | }
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/src/main/java/org/ehcache/sizeof/SizeOfFilterSource.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof;
18 |
19 | import org.ehcache.sizeof.filters.AnnotationSizeOfFilter;
20 | import org.ehcache.sizeof.filters.SizeOfFilter;
21 | import org.ehcache.sizeof.filters.TypeFilter;
22 |
23 | import java.lang.reflect.Field;
24 | import java.lang.reflect.Modifier;
25 | import java.util.ArrayList;
26 | import java.util.List;
27 | import java.util.ServiceLoader;
28 | import java.util.concurrent.CopyOnWriteArrayList;
29 |
30 | /**
31 | * @author Alex Snaps
32 | */
33 | public final class SizeOfFilterSource implements Filter {
34 |
35 | private final CopyOnWriteArrayList filters = new CopyOnWriteArrayList<>();
36 | private final TypeFilter typeFilter = new TypeFilter();
37 |
38 | public SizeOfFilterSource(boolean registerAnnotationFilter) {
39 | filters.add(typeFilter);
40 | if (registerAnnotationFilter) {
41 | filters.add(new AnnotationSizeOfFilter());
42 | }
43 | applyMutators();
44 | }
45 |
46 | private void applyMutators() {
47 | applyMutators(SizeOfFilterSource.class.getClassLoader());
48 | }
49 |
50 | void applyMutators(final ClassLoader classLoader) {
51 | final ServiceLoader loader = ServiceLoader.load(FilterConfigurator.class, classLoader);
52 | for (FilterConfigurator filterConfigurator : loader) {
53 | filterConfigurator.configure(this);
54 | }
55 | }
56 |
57 | public SizeOfFilter[] getFilters() {
58 | List allFilters = new ArrayList<>(filters);
59 | return allFilters.toArray(new SizeOfFilter[allFilters.size()]);
60 | }
61 |
62 | public void ignoreInstancesOf(final Class clazz, final boolean strict) {
63 | typeFilter.addClass(clazz, Modifier.isFinal(clazz.getModifiers()) || strict);
64 | }
65 |
66 | public void ignoreField(final Field field) {
67 | typeFilter.addField(field);
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/org/ehcache/sizeof/VisitorListener.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof;
18 |
19 | /**
20 | * @author Alex Snaps
21 | */
22 | public interface VisitorListener {
23 |
24 | void visited(final Object object, final long size);
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/org/ehcache/sizeof/annotations/AnnotationProxyFactory.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof.annotations;
18 |
19 | import java.lang.annotation.Annotation;
20 | import java.lang.reflect.InvocationHandler;
21 | import java.lang.reflect.Method;
22 | import java.lang.reflect.Proxy;
23 |
24 | /**
25 | * Allows you to transform the type of your custom annotation to a reference annotation type.
26 | * It can come handy when you want to allow the consumers of your library not to depend on your API because of the annotations, still allowing them to use the original annotation methods.
27 | *
28 | * Example :
29 | *
30 | * //getting a custom annotation from a class
31 | * my.Annotation customAnnotation = klazz.getAnnotation(my.Annotation.class);
32 | * //if this annotation is "similar" (duck-typing, same methods) to the reference one, I can get a proxy to it, whose type is the reference annotation
33 | * ehcache.Annotation annotation = AnnotationProxyFactory.getAnnotationProxy(customAnnotation, ehcache.Annotation.class);
34 | *
35 | * //so my library can apply the behavior when the default annotation is used
36 | *
37 | * @author Anthony Dahanne
38 | * @ehcache.Annotation(action="true") public class UserClass {}
39 | *
40 | * //or when a custom one is used, since all calls to action() will be caught and redirected to the custom annotation action method, if it exists,
41 | * //or fall back to the reference action method
42 | * @my.Annotation(action="true") public class UserClass {}
43 | */
44 | public final class AnnotationProxyFactory {
45 |
46 |
47 | private AnnotationProxyFactory() {
48 | //not to instantiate
49 | }
50 |
51 | /**
52 | * Returns a proxy on the customAnnotation, having the same type than the referenceAnnotation
53 | *
54 | * @param customAnnotation annotation proxied
55 | * @param referenceAnnotation type of the returned annotation
56 | * @return proxied customAnnotation with the type of referenceAnnotation
57 | */
58 | @SuppressWarnings("unchecked")
59 | public static T getAnnotationProxy(Annotation customAnnotation, Class referenceAnnotation) {
60 | InvocationHandler handler = new AnnotationInvocationHandler(customAnnotation);
61 | return (T)Proxy.newProxyInstance(referenceAnnotation.getClassLoader(), new Class[] { referenceAnnotation }, handler);
62 | }
63 |
64 | /**
65 | * Invocation handler implementing an invoke method that redirects every method call to the custom annotation method
66 | * when possible; if not returns the reference annotation method default value
67 | */
68 | private static class AnnotationInvocationHandler implements InvocationHandler {
69 |
70 | private final Annotation customAnnotation;
71 |
72 | public AnnotationInvocationHandler(Annotation customAnnotation) {
73 | this.customAnnotation = customAnnotation;
74 | }
75 |
76 | public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
77 | //trying to call the method on the custom annotation, if it exists
78 | Method methodOnCustom = getMatchingMethodOnGivenAnnotation(method);
79 | if (methodOnCustom != null) {
80 | return methodOnCustom.invoke(customAnnotation, args);
81 | } else {
82 | //otherwise getting the default value of the reference annotation method
83 | Object defaultValue = method.getDefaultValue();
84 | if (defaultValue != null) {
85 | return defaultValue;
86 | }
87 | throw new UnsupportedOperationException(
88 | "The method \""
89 | + method.getName()
90 | + "\" does not exist in the custom annotation, and there is no default value for"
91 | + " it in the reference annotation, please implement this method in your custom annotation.");
92 | }
93 | }
94 |
95 | private Method getMatchingMethodOnGivenAnnotation(Method method) {
96 | try {
97 | Method customMethod = customAnnotation.getClass().getDeclaredMethod(method.getName(), method.getParameterTypes());
98 | if (customMethod.getReturnType().isAssignableFrom(method.getReturnType())) {
99 | return customMethod;
100 | }
101 | return null;
102 | } catch (NoSuchMethodException e) {
103 | return null;
104 | }
105 | }
106 | }
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/src/main/java/org/ehcache/sizeof/annotations/IgnoreSizeOf.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof.annotations;
18 |
19 | import java.lang.annotation.ElementType;
20 | import java.lang.annotation.Retention;
21 | import java.lang.annotation.RetentionPolicy;
22 | import java.lang.annotation.Target;
23 |
24 | /**
25 | * Annotation to ignore a field, type or entire package while doing a SizeOf measurement
26 | *
27 | * @author Chris Dennis
28 | * @see org.ehcache.sizeof.SizeOf
29 | */
30 | @Retention(RetentionPolicy.RUNTIME)
31 | @Target({ ElementType.FIELD, ElementType.TYPE, ElementType.PACKAGE })
32 | public @interface IgnoreSizeOf {
33 |
34 | /**
35 | * Controls whether the annotation, when applied to a {@link ElementType#TYPE type} is to be applied to all its subclasses
36 | * as well or solely on that type only. true if inherited by subtypes, false otherwise
37 | */
38 | boolean inherited() default false;
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/org/ehcache/sizeof/filters/AnnotationSizeOfFilter.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof.filters;
18 |
19 | import org.ehcache.sizeof.annotations.AnnotationProxyFactory;
20 | import org.ehcache.sizeof.annotations.IgnoreSizeOf;
21 | import org.slf4j.Logger;
22 | import org.slf4j.LoggerFactory;
23 |
24 | import java.lang.annotation.Annotation;
25 | import java.lang.reflect.AnnotatedElement;
26 | import java.lang.reflect.Field;
27 | import java.util.Collection;
28 | import java.util.Iterator;
29 | import java.util.regex.Matcher;
30 | import java.util.regex.Pattern;
31 | import java.util.regex.PatternSyntaxException;
32 |
33 | public final class AnnotationSizeOfFilter implements SizeOfFilter {
34 |
35 | private static final String IGNORE_SIZE_OF_VM_ARGUMENT = AnnotationSizeOfFilter.class.getName() + ".pattern";
36 |
37 | private static final Logger LOG = LoggerFactory.getLogger(AnnotationSizeOfFilter.class.getName());
38 |
39 | //default is *cache.*IgnoreSizeOf
40 | private static final String IGNORE_SIZE_OF_DEFAULT_REGEXP = "^.*cache\\..*IgnoreSizeOf$";
41 | private static final Pattern IGNORE_SIZE_OF_PATTERN;
42 |
43 | static {
44 | String ignoreSizeOfRegexpVMArg = System.getProperty(IGNORE_SIZE_OF_VM_ARGUMENT);
45 | String ignoreSizeOfRegexp = ignoreSizeOfRegexpVMArg != null ? ignoreSizeOfRegexpVMArg : IGNORE_SIZE_OF_DEFAULT_REGEXP;
46 | Pattern localPattern;
47 | try {
48 | localPattern = Pattern.compile(ignoreSizeOfRegexp);
49 | LOG.info("Using regular expression provided through VM argument "
50 | + IGNORE_SIZE_OF_VM_ARGUMENT
51 | + " for IgnoreSizeOf annotation : "
52 | + ignoreSizeOfRegexp);
53 | } catch (PatternSyntaxException e) {
54 | throw new IllegalArgumentException("Invalid regular expression provided through VM argument "
55 | + IGNORE_SIZE_OF_VM_ARGUMENT
56 | + " : \n"
57 | + e.getMessage()
58 | + "\n using default regular expression for IgnoreSizeOf annotation : "
59 | + IGNORE_SIZE_OF_DEFAULT_REGEXP);
60 | }
61 | IGNORE_SIZE_OF_PATTERN = localPattern;
62 | }
63 |
64 | /**
65 | * {@inheritDoc}
66 | */
67 | public Collection filterFields(Class> klazz, Collection fields) {
68 | for (Iterator it = fields.iterator(); it.hasNext(); ) {
69 |
70 | Field field = it.next();
71 | IgnoreSizeOf annotationOnField = getAnnotationOn(field, IgnoreSizeOf.class, IGNORE_SIZE_OF_PATTERN);
72 | if (annotationOnField != null) {
73 | it.remove();
74 | }
75 | }
76 | return fields;
77 | }
78 |
79 | /**
80 | * {@inheritDoc}
81 | */
82 | public boolean filterClass(Class> klazz) {
83 | boolean classAnnotated = isAnnotationPresentOrInherited(klazz);
84 | Package pack = klazz.getPackage();
85 | IgnoreSizeOf annotationOnPackage = pack == null ? null : getAnnotationOn(pack, IgnoreSizeOf.class, IGNORE_SIZE_OF_PATTERN);
86 | boolean packageAnnotated = annotationOnPackage != null;
87 | return !classAnnotated && !packageAnnotated;
88 | }
89 |
90 | private boolean isAnnotationPresentOrInherited(final Class> instanceKlazz) {
91 | Class> klazz = instanceKlazz;
92 | while (klazz != null) {
93 | IgnoreSizeOf annotationOnClass = getAnnotationOn(klazz, IgnoreSizeOf.class, IGNORE_SIZE_OF_PATTERN);
94 | if (annotationOnClass != null && (klazz == instanceKlazz || annotationOnClass.inherited())) {
95 | return true;
96 | }
97 | klazz = klazz.getSuperclass();
98 | }
99 | return false;
100 | }
101 |
102 | private boolean validateCustomAnnotationPattern(String canonicalName, Pattern matchingAnnotationPattern) {
103 | Matcher matcher = matchingAnnotationPattern.matcher(canonicalName);
104 |
105 | boolean found = matcher.matches();
106 | if (found) {
107 | LOG.debug(canonicalName + " matched IgnoreSizeOf annotation pattern " + IGNORE_SIZE_OF_PATTERN);
108 | }
109 | return found;
110 | }
111 |
112 | //EHC-938 : looking for all types of IgnoreSizeOf annotations
113 | private T getAnnotationOn(AnnotatedElement element, Class referenceAnnotation, Pattern matchingAnnotationPattern) {
114 | T matchingAnnotation = null;
115 | Annotation[] annotations = element.getAnnotations();
116 | for (Annotation annotation : annotations) {
117 | if (validateCustomAnnotationPattern(annotation.annotationType().getName(), matchingAnnotationPattern)) {
118 | if (matchingAnnotation != null) {
119 | throw new IllegalStateException("You are not allowed to use more than one @" + referenceAnnotation.getName()
120 | + " annotations for the same element : "
121 | + element.toString());
122 | }
123 | matchingAnnotation = AnnotationProxyFactory.getAnnotationProxy(annotation, referenceAnnotation);
124 | }
125 | }
126 | return matchingAnnotation;
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/src/main/java/org/ehcache/sizeof/filters/CombinationSizeOfFilter.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof.filters;
18 |
19 | import java.lang.reflect.Field;
20 | import java.util.Collection;
21 |
22 | /**
23 | * Filter combining multiple filters
24 | *
25 | * @author Chris Dennis
26 | */
27 | public class CombinationSizeOfFilter implements SizeOfFilter {
28 |
29 | private final SizeOfFilter[] filters;
30 |
31 | /**
32 | * Constructs a filter combining multiple ones
33 | *
34 | * @param filters the filters to combine
35 | */
36 | public CombinationSizeOfFilter(SizeOfFilter... filters) {
37 | this.filters = filters;
38 | }
39 |
40 | /**
41 | * {@inheritDoc}
42 | */
43 | public Collection filterFields(Class> klazz, Collection fields) {
44 | Collection current = fields;
45 | for (SizeOfFilter filter : filters) {
46 | current = filter.filterFields(klazz, current);
47 | }
48 | return current;
49 | }
50 |
51 | /**
52 | * {@inheritDoc}
53 | */
54 | public boolean filterClass(Class> klazz) {
55 | for (SizeOfFilter filter : filters) {
56 | if (!filter.filterClass(klazz)) {
57 | return false;
58 | }
59 | }
60 | return true;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/org/ehcache/sizeof/filters/SizeOfFilter.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof.filters;
18 |
19 | import java.lang.reflect.Field;
20 | import java.util.Collection;
21 |
22 | /**
23 | * Filter to filter types or fields of object graphs passed to a SizeOf engine
24 | *
25 | * @author Chris Dennis
26 | * @see org.ehcache.sizeof.SizeOf
27 | */
28 | public interface SizeOfFilter {
29 |
30 | /**
31 | * Returns the fields to walk and measure for a type
32 | *
33 | * @param klazz the type
34 | * @param fields the fields already "qualified"
35 | * @return the filtered Set
36 | */
37 | Collection filterFields(Class> klazz, Collection fields);
38 |
39 | /**
40 | * Checks whether the type needs to be filtered
41 | *
42 | * @param klazz the type
43 | * @return true, if to be filtered out
44 | */
45 | boolean filterClass(Class> klazz);
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/org/ehcache/sizeof/filters/TypeFilter.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof.filters;
18 |
19 | import org.ehcache.sizeof.util.WeakIdentityConcurrentMap;
20 |
21 | import java.lang.reflect.Field;
22 | import java.util.Collection;
23 | import java.util.concurrent.ConcurrentHashMap;
24 | import java.util.concurrent.ConcurrentMap;
25 |
26 | /**
27 | * @author Alex Snaps
28 | */
29 | public class TypeFilter implements SizeOfFilter {
30 |
31 | private final WeakIdentityConcurrentMap, Object> classesIgnored = new WeakIdentityConcurrentMap<>();
32 | private final WeakIdentityConcurrentMap, Object> superClasses = new WeakIdentityConcurrentMap<>();
33 | private final WeakIdentityConcurrentMap, ConcurrentMap> fieldsIgnored = new WeakIdentityConcurrentMap<>();
34 |
35 | @Override
36 | public Collection filterFields(final Class> klazz, final Collection fields) {
37 | final ConcurrentMap fieldsToIgnore = fieldsIgnored.get(klazz);
38 | if (fieldsToIgnore != null) {
39 | fields.removeIf(fieldsToIgnore::containsKey);
40 | }
41 | return fields;
42 | }
43 |
44 | @Override
45 | public boolean filterClass(final Class> klazz) {
46 | if (!classesIgnored.containsKey(klazz)) {
47 | for (Class> aClass : superClasses.keySet()) {
48 | if (aClass.isAssignableFrom(klazz)) {
49 | classesIgnored.put(klazz, this);
50 | return false;
51 | }
52 | }
53 | return true;
54 | } else {
55 | return false;
56 | }
57 | }
58 |
59 | public void addClass(final Class> classToFilterOut, final boolean strict) {
60 | if (!strict) {
61 | superClasses.putIfAbsent(classToFilterOut, this);
62 | } else {
63 | classesIgnored.put(classToFilterOut, this);
64 | }
65 | }
66 |
67 | public void addField(final Field fieldToFilterOut) {
68 | final Class> klazz = fieldToFilterOut.getDeclaringClass();
69 | ConcurrentMap fields = fieldsIgnored.get(klazz);
70 | if (fields == null) {
71 | fields = new ConcurrentHashMap<>();
72 | final ConcurrentMap previous = fieldsIgnored.putIfAbsent(klazz, fields);
73 | if (previous != null) {
74 | fields = previous;
75 | }
76 | }
77 | fields.put(fieldToFilterOut, this);
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/main/java/org/ehcache/sizeof/impl/AgentLoader.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof.impl;
18 |
19 |
20 | import org.slf4j.Logger;
21 | import org.slf4j.LoggerFactory;
22 |
23 | import java.io.File;
24 | import java.io.FileOutputStream;
25 | import java.io.IOException;
26 | import java.io.InputStream;
27 | import java.lang.instrument.Instrumentation;
28 | import java.lang.management.ManagementFactory;
29 | import java.lang.reflect.InvocationTargetException;
30 | import java.lang.reflect.Method;
31 | import java.net.URL;
32 | import java.net.URLClassLoader;
33 | import java.security.AccessController;
34 | import java.security.PrivilegedActionException;
35 | import java.security.PrivilegedExceptionAction;
36 | import java.util.ArrayList;
37 | import java.util.List;
38 |
39 | /**
40 | * This will try to load the agent using the Attach API of JDK6.
41 | * If you are on an older JDK (v5) you can still use the agent by adding the -javaagent:[pathTojar] to your VM
42 | * startup script
43 | *
44 | * @author Alex Snaps
45 | */
46 | final class AgentLoader {
47 |
48 | private static final Logger LOGGER = LoggerFactory.getLogger(AgentLoader.class);
49 |
50 | private static final String SIZEOF_AGENT_CLASSNAME = "org.ehcache.sizeof.impl.SizeOfAgent";
51 | private static final String VIRTUAL_MACHINE_CLASSNAME = "com.sun.tools.attach.VirtualMachine";
52 | private static final Method VIRTUAL_MACHINE_ATTACH;
53 | private static final Method VIRTUAL_MACHINE_DETACH;
54 | private static final Method VIRTUAL_MACHINE_LOAD_AGENT;
55 |
56 | private static volatile Instrumentation instrumentation;
57 |
58 | static final String INSTRUMENTATION_INSTANCE_SYSTEM_PROPERTY_NAME = "org.ehcache.sizeof.agent.instrumentation";
59 |
60 | static {
61 | Method attach = null;
62 | Method detach = null;
63 | Method loadAgent = null;
64 | try {
65 | Class> virtualMachineClass = getVirtualMachineClass();
66 | attach = virtualMachineClass.getMethod("attach", String.class);
67 | detach = virtualMachineClass.getMethod("detach");
68 | loadAgent = virtualMachineClass.getMethod("loadAgent", String.class);
69 | } catch (Throwable e) {
70 | LOGGER.info("Unavailable or unrecognised attach API : {}", e.toString());
71 | }
72 | VIRTUAL_MACHINE_ATTACH = attach;
73 | VIRTUAL_MACHINE_DETACH = detach;
74 | VIRTUAL_MACHINE_LOAD_AGENT = loadAgent;
75 | }
76 |
77 | private static Class> getVirtualMachineClass() throws ClassNotFoundException {
78 | try {
79 | return AccessController.doPrivileged((PrivilegedExceptionAction>) () -> {
80 | try {
81 | return ClassLoader.getSystemClassLoader().loadClass(VIRTUAL_MACHINE_CLASSNAME);
82 | } catch (ClassNotFoundException cnfe) {
83 | for (File jar : getPossibleToolsJars()) {
84 | try {
85 | Class> vmClass = new URLClassLoader(new URL[] { jar.toURI().toURL() }).loadClass(VIRTUAL_MACHINE_CLASSNAME);
86 | LOGGER.info("Located valid 'tools.jar' at '{}'", jar);
87 | return vmClass;
88 | } catch (Throwable t) {
89 | LOGGER.info("Exception while loading tools.jar from '{}': {}", jar, t);
90 | }
91 | }
92 | throw new ClassNotFoundException(VIRTUAL_MACHINE_CLASSNAME);
93 | }
94 | });
95 | } catch (PrivilegedActionException pae) {
96 | Throwable actual = pae.getCause();
97 | if (actual instanceof ClassNotFoundException) {
98 | throw (ClassNotFoundException)actual;
99 | }
100 | throw new AssertionError("Unexpected checked exception : " + actual);
101 | }
102 | }
103 |
104 | private static List getPossibleToolsJars() {
105 | List jars = new ArrayList<>();
106 |
107 | File javaHome = new File(System.getProperty("java.home"));
108 | File jreSourced = new File(javaHome, "lib/tools.jar");
109 | if (jreSourced.exists()) {
110 | jars.add(jreSourced);
111 | }
112 | if ("jre".equals(javaHome.getName())) {
113 | File jdkHome = new File(javaHome, "../");
114 | File jdkSourced = new File(jdkHome, "lib/tools.jar");
115 | if (jdkSourced.exists()) {
116 | jars.add(jdkSourced);
117 | }
118 | }
119 | return jars;
120 | }
121 |
122 | /**
123 | * Attempts to load the agent through the Attach API
124 | *
125 | * @return true if agent was loaded (which could have happened thought the -javaagent switch)
126 | */
127 | static boolean loadAgent() {
128 | synchronized (AgentLoader.class.getName().intern()) {
129 | if (!agentIsAvailable() && VIRTUAL_MACHINE_LOAD_AGENT != null) {
130 | try {
131 | warnIfOSX();
132 | String name = ManagementFactory.getRuntimeMXBean().getName();
133 | Object vm = VIRTUAL_MACHINE_ATTACH.invoke(null, name.substring(0, name.indexOf('@')));
134 | try {
135 | File agent = getAgentFile();
136 | LOGGER.info("Trying to load agent @ {}", agent);
137 | if (agent != null) {
138 | VIRTUAL_MACHINE_LOAD_AGENT.invoke(vm, agent.getAbsolutePath());
139 | }
140 | } finally {
141 | VIRTUAL_MACHINE_DETACH.invoke(vm);
142 | }
143 | } catch (InvocationTargetException ite) {
144 | Throwable cause = ite.getCause();
145 | LOGGER.info("Failed to attach to VM and load the agent: {}: {}", cause.getClass(), cause.getMessage());
146 | } catch (Throwable t) {
147 | LOGGER.info("Failed to attach to VM and load the agent: {}: {}", t.getClass(), t.getMessage());
148 | }
149 | }
150 | final boolean b = agentIsAvailable();
151 | if (b) {
152 | LOGGER.info("Agent successfully loaded and available!");
153 | }
154 |
155 | return b;
156 | }
157 | }
158 |
159 | private static void warnIfOSX() {
160 | if (JvmInformation.isOSX() && System.getProperty("java.io.tmpdir") != null) {
161 | LOGGER.warn("Loading the SizeOfAgent will probably fail, as you are running on Apple OS X and have a value set for java.io.tmpdir\n" +
162 | "They both result in a bug, not yet fixed by Apple, that won't let us attach to the VM and load the agent.\n" +
163 | "Most probably, you'll also get a full thread-dump after this because of the failure... Nothing to worry about!\n" +
164 | "You can bypass trying to load the Agent entirely by setting the System property '"
165 | + AgentSizeOf.BYPASS_LOADING + "' to true");
166 | }
167 | }
168 |
169 | private static File getAgentFile() throws IOException {
170 | URL agent = AgentLoader.class.getResource("sizeof-agent.jar");
171 | if (agent == null) {
172 | return null;
173 | } else if (agent.getProtocol().equals("file")) {
174 | return new File(agent.getFile());
175 | } else {
176 | File temp = File.createTempFile("ehcache-sizeof-agent", ".jar");
177 | try (FileOutputStream fout = new FileOutputStream(temp); InputStream in = agent.openStream()) {
178 | byte[] buffer = new byte[1024];
179 | while (true) {
180 | int read = in.read(buffer);
181 | if (read < 0) {
182 | break;
183 | } else {
184 | fout.write(buffer, 0, read);
185 | }
186 | }
187 | } finally {
188 | temp.deleteOnExit();
189 | }
190 | LOGGER.info("Extracted agent jar to temporary file {}", temp);
191 | return temp;
192 | }
193 | }
194 |
195 | /**
196 | * Checks whether the agent is available
197 | *
198 | * @return true if available
199 | */
200 | static boolean agentIsAvailable() {
201 | try {
202 | if (instrumentation == null) {
203 | instrumentation = (Instrumentation)System.getProperties().get(INSTRUMENTATION_INSTANCE_SYSTEM_PROPERTY_NAME);
204 | }
205 | if (instrumentation == null) {
206 | Class> sizeOfAgentClass = ClassLoader.getSystemClassLoader().loadClass(SIZEOF_AGENT_CLASSNAME);
207 | Method getInstrumentationMethod = sizeOfAgentClass.getMethod("getInstrumentation");
208 | instrumentation = (Instrumentation)getInstrumentationMethod.invoke(sizeOfAgentClass);
209 | }
210 | return instrumentation != null;
211 | } catch (SecurityException e) {
212 | LOGGER.warn("Couldn't access the system classloader because of the security policies applied by " +
213 | "the security manager. You either want to loosen these, so ClassLoader.getSystemClassLoader() and " +
214 | "reflection API calls are permitted or the sizing will be done using some other mechanism.\n" +
215 | "Alternatively, set the system property org.ehcache.sizeof.agent.instrumentationSystemProperty to true " +
216 | "to have the agent put the required instances in the System Properties for the loader to access.");
217 | return false;
218 | } catch (Throwable e) {
219 | return false;
220 | }
221 | }
222 |
223 | /**
224 | * Returns the size of this Java object as calculated by the loaded agent.
225 | *
226 | * @param obj object to be sized
227 | * @return size of the object in bytes
228 | */
229 | static long agentSizeOf(Object obj) {
230 | if (instrumentation == null) {
231 | throw new UnsupportedOperationException("Sizeof agent is not available");
232 | }
233 | return instrumentation.getObjectSize(obj);
234 | }
235 | }
236 |
--------------------------------------------------------------------------------
/src/main/java/org/ehcache/sizeof/impl/AgentSizeOf.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof.impl;
18 |
19 | import org.ehcache.sizeof.SizeOf;
20 | import org.ehcache.sizeof.filters.SizeOfFilter;
21 |
22 | import static org.ehcache.sizeof.impl.JvmInformation.CURRENT_JVM_INFORMATION;
23 |
24 | /**
25 | * SizeOf implementation that relies on a Java agent to be loaded to do the measurement
26 | * It will try to load the agent through the JDK6 Attach API if available
27 | * All it's constructor do throw UnsupportedOperationException if the agent isn't present or couldn't be loaded dynamically
28 | *
29 | * Inspired by Dr. Heinz Kabutz's Java Specialist Newsletter Issue #142
30 | *
31 | * @author Chris Dennis
32 | * @author Alex Snaps
33 | *
34 | * @link http://www.javaspecialists.eu/archive/Issue142.html
35 | */
36 | public class AgentSizeOf extends SizeOf {
37 |
38 | /**
39 | * System property name to bypass attaching to the VM and loading of Java agent to measure Object sizes.
40 | */
41 | public static final String BYPASS_LOADING = "org.ehcache.sizeof.AgentSizeOf.bypass";
42 |
43 | private static final boolean AGENT_LOADED = !Boolean.getBoolean(BYPASS_LOADING) && AgentLoader.loadAgent();
44 |
45 | /**
46 | * Builds a new SizeOf that will not filter fields and will cache reflected fields
47 | *
48 | * @throws UnsupportedOperationException If agent couldn't be loaded or isn't present
49 | * @see #AgentSizeOf(SizeOfFilter, boolean, boolean)
50 | */
51 | public AgentSizeOf() throws UnsupportedOperationException {
52 | this(new PassThroughFilter());
53 | }
54 |
55 | /**
56 | * Builds a new SizeOf that will filter fields according to the provided filter and will cache reflected fields
57 | *
58 | * @param filter The filter to apply
59 | * @throws UnsupportedOperationException If agent couldn't be loaded or isn't present
60 | * @see #AgentSizeOf(SizeOfFilter, boolean, boolean)
61 | * @see org.ehcache.sizeof.filters.SizeOfFilter
62 | */
63 | public AgentSizeOf(SizeOfFilter filter) throws UnsupportedOperationException {
64 | this(filter, true, true);
65 | }
66 |
67 | /**
68 | * Builds a new SizeOf that will filter fields according to the provided filter
69 | *
70 | * @param filter The filter to apply
71 | * @param caching whether to cache reflected fields
72 | * @param bypassFlyweight whether "Flyweight Objects" are to be ignored
73 | * @throws UnsupportedOperationException If agent couldn't be loaded or isn't present
74 | * @see SizeOfFilter
75 | */
76 | public AgentSizeOf(SizeOfFilter filter, boolean caching, boolean bypassFlyweight) throws UnsupportedOperationException {
77 | super(filter, caching, bypassFlyweight);
78 | if (!AGENT_LOADED) {
79 | throw new UnsupportedOperationException("Agent not available or loadable");
80 | }
81 | }
82 |
83 | @Override
84 | public long sizeOf(Object obj) {
85 | final long measuredSize = AgentLoader.agentSizeOf(obj);
86 | return Math.max(CURRENT_JVM_INFORMATION.getMinimumObjectSize(),
87 | measuredSize + CURRENT_JVM_INFORMATION.getAgentSizeOfAdjustment());
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/main/java/org/ehcache/sizeof/impl/PassThroughFilter.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof.impl;
18 |
19 | import org.ehcache.sizeof.filters.SizeOfFilter;
20 |
21 | import java.lang.reflect.Field;
22 | import java.util.Collection;
23 |
24 | /**
25 | * @author Alex Snaps
26 | */
27 | public class PassThroughFilter implements SizeOfFilter {
28 |
29 | /**
30 | * {@inheritDoc}
31 | */
32 | public Collection filterFields(Class> klazz, Collection fields) {
33 | return fields;
34 | }
35 |
36 | /**
37 | * {@inheritDoc}
38 | */
39 | public boolean filterClass(Class> klazz) {
40 | return true;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/org/ehcache/sizeof/impl/PrimitiveType.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof.impl;
18 |
19 | import static org.ehcache.sizeof.impl.JvmInformation.CURRENT_JVM_INFORMATION;
20 |
21 | /**
22 | * Primitive types in the VM type system and their sizes
23 | *
24 | * @author Alex Snaps
25 | */
26 | enum PrimitiveType {
27 |
28 | /**
29 | * boolean.class
30 | */
31 | BOOLEAN(boolean.class, 1),
32 | /**
33 | * byte.class
34 | */
35 | BYTE(byte.class, 1),
36 | /**
37 | * char.class
38 | */
39 | CHAR(char.class, 2),
40 | /**
41 | * short.class
42 | */
43 | SHORT(short.class, 2),
44 | /**
45 | * int.class
46 | */
47 | INT(int.class, 4),
48 | /**
49 | * float.class
50 | */
51 | FLOAT(float.class, 4),
52 | /**
53 | * double.class
54 | */
55 | DOUBLE(double.class, 8),
56 | /**
57 | * long.class
58 | */
59 | LONG(long.class, 8);
60 |
61 | private final Class> type;
62 | private final int size;
63 |
64 |
65 | PrimitiveType(Class> type, int size) {
66 | this.type = type;
67 | this.size = size;
68 | }
69 |
70 | /**
71 | * Returns the size in memory this type occupies
72 | *
73 | * @return size in bytes
74 | */
75 | public int getSize() {
76 | return size;
77 | }
78 |
79 | /**
80 | * The representing type
81 | *
82 | * @return the type
83 | */
84 | public Class> getType() {
85 | return type;
86 | }
87 |
88 | /**
89 | * The size of a pointer
90 | *
91 | * @return size in bytes
92 | */
93 | public static int getReferenceSize() {
94 | return CURRENT_JVM_INFORMATION.getJavaPointerSize();
95 | }
96 |
97 | /**
98 | * The size on an array
99 | *
100 | * @return size in bytes
101 | */
102 | public static long getArraySize() {
103 | return CURRENT_JVM_INFORMATION.getObjectHeaderSize() + INT.getSize();
104 | }
105 |
106 | /**
107 | * Finds the matching PrimitiveType for a type
108 | *
109 | * @param type the type to find the PrimitiveType for
110 | * @return the PrimitiveType instance or null if none found
111 | */
112 | public static PrimitiveType forType(final Class> type) {
113 | for (PrimitiveType primitiveType : values()) {
114 | if (primitiveType.getType() == type) {
115 | return primitiveType;
116 | }
117 | }
118 | return null;
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/src/main/java/org/ehcache/sizeof/impl/ReflectionSizeOf.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof.impl;
18 |
19 | import org.ehcache.sizeof.SizeOf;
20 | import org.ehcache.sizeof.filters.SizeOfFilter;
21 | import org.slf4j.Logger;
22 | import org.slf4j.LoggerFactory;
23 |
24 | import java.lang.reflect.Array;
25 | import java.lang.reflect.Field;
26 | import java.lang.reflect.Modifier;
27 | import java.util.ArrayDeque;
28 | import java.util.Deque;
29 |
30 | import static org.ehcache.sizeof.impl.JvmInformation.CURRENT_JVM_INFORMATION;
31 |
32 | /**
33 | * SizeOf that uses reflection to measure on heap size of object graphs
34 | * Inspired by Dr. Heinz Kabutz's Java Specialist Newsletter Issue #78
35 | *
36 | * @author Alex Snaps
37 | * @author Chris Dennis
38 | *
39 | * @link http://www.javaspecialists.eu/archive/Issue078.html
40 | */
41 | public class ReflectionSizeOf extends SizeOf {
42 |
43 | private static final Logger LOGGER = LoggerFactory.getLogger(ReflectionSizeOf.class);
44 |
45 | /**
46 | * Builds a new SizeOf that will not filter fields and will cache reflected fields
47 | *
48 | * @see #ReflectionSizeOf(SizeOfFilter, boolean, boolean)
49 | */
50 | public ReflectionSizeOf() {
51 | this(new PassThroughFilter());
52 | }
53 |
54 | /**
55 | * Builds a new SizeOf that will filter fields and will cache reflected fields
56 | *
57 | * @param fieldFilter The filter to apply
58 | * @see #ReflectionSizeOf(SizeOfFilter, boolean, boolean)
59 | * @see SizeOfFilter
60 | */
61 | public ReflectionSizeOf(SizeOfFilter fieldFilter) {
62 | this(fieldFilter, true, true);
63 | }
64 |
65 | /**
66 | * Builds a new SizeOf that will filter fields
67 | *
68 | * @param fieldFilter The filter to apply
69 | * @param caching Whether to cache reflected fields
70 | * @param bypassFlyweight whether "Flyweight Objects" are to be ignored
71 | * @see SizeOfFilter
72 | */
73 | public ReflectionSizeOf(SizeOfFilter fieldFilter, boolean caching, boolean bypassFlyweight) {
74 | super(fieldFilter, caching, bypassFlyweight);
75 |
76 | if (!CURRENT_JVM_INFORMATION.supportsReflectionSizeOf()) {
77 | LOGGER.warn("ReflectionSizeOf is not always accurate on the JVM (" + CURRENT_JVM_INFORMATION.getJvmDescription() +
78 | "). Please consider enabling AgentSizeOf.");
79 | }
80 | }
81 |
82 | /**
83 | * {@inheritDoc}
84 | */
85 | @Override
86 | public long sizeOf(Object obj) {
87 | if (obj == null) {
88 | return 0;
89 | }
90 |
91 | Class> aClass = obj.getClass();
92 | if (aClass.isArray()) {
93 | return guessArraySize(obj);
94 | } else {
95 | long size = CURRENT_JVM_INFORMATION.getObjectHeaderSize();
96 |
97 | Deque> classStack = new ArrayDeque<>();
98 | for (Class> klazz = aClass; klazz != null; klazz = klazz.getSuperclass()) {
99 | classStack.push(klazz);
100 | }
101 |
102 | while (!classStack.isEmpty()) {
103 | Class> klazz = classStack.pop();
104 |
105 | //assuming default class layout
106 | int oops = 0;
107 | int doubles = 0;
108 | int words = 0;
109 | int shorts = 0;
110 | int bytes = 0;
111 | for (Field f : klazz.getDeclaredFields()) {
112 | if (Modifier.isStatic(f.getModifiers())) {
113 | continue;
114 | }
115 | if (f.getType().isPrimitive()) {
116 | switch (PrimitiveType.forType(f.getType())) {
117 | case BOOLEAN:
118 | case BYTE:
119 | bytes++;
120 | break;
121 | case SHORT:
122 | case CHAR:
123 | shorts++;
124 | break;
125 | case INT:
126 | case FLOAT:
127 | words++;
128 | break;
129 | case DOUBLE:
130 | case LONG:
131 | doubles++;
132 | break;
133 | default:
134 | throw new AssertionError();
135 | }
136 | } else {
137 | oops++;
138 | }
139 | }
140 | if (doubles > 0 && (size % PrimitiveType.LONG.getSize()) != 0) {
141 | long length = PrimitiveType.LONG.getSize() - (size % PrimitiveType.LONG.getSize());
142 | size += PrimitiveType.LONG.getSize() - (size % PrimitiveType.LONG.getSize());
143 |
144 | while (length >= PrimitiveType.INT.getSize() && words > 0) {
145 | length -= PrimitiveType.INT.getSize();
146 | words--;
147 | }
148 | while (length >= PrimitiveType.SHORT.getSize() && shorts > 0) {
149 | length -= PrimitiveType.SHORT.getSize();
150 | shorts--;
151 | }
152 | while (length >= PrimitiveType.BYTE.getSize() && bytes > 0) {
153 | length -= PrimitiveType.BYTE.getSize();
154 | bytes--;
155 | }
156 | while (length >= PrimitiveType.getReferenceSize() && oops > 0) {
157 | length -= PrimitiveType.getReferenceSize();
158 | oops--;
159 | }
160 | }
161 | size += PrimitiveType.DOUBLE.getSize() * doubles;
162 | size += PrimitiveType.INT.getSize() * words;
163 | size += PrimitiveType.SHORT.getSize() * shorts;
164 | size += PrimitiveType.BYTE.getSize() * bytes;
165 |
166 | if (oops > 0) {
167 | if ((size % PrimitiveType.getReferenceSize()) != 0) {
168 | size += PrimitiveType.getReferenceSize() - (size % PrimitiveType.getReferenceSize());
169 | }
170 | size += oops * PrimitiveType.getReferenceSize();
171 | }
172 |
173 | if ((doubles + words + shorts + bytes + oops) > 0 && (size % PrimitiveType.getReferenceSize()) != 0) {
174 | size += PrimitiveType.getReferenceSize() - (size % PrimitiveType.getReferenceSize());
175 | }
176 | }
177 | if ((size % CURRENT_JVM_INFORMATION.getObjectAlignment()) != 0) {
178 | size += CURRENT_JVM_INFORMATION.getObjectAlignment() - (size % CURRENT_JVM_INFORMATION.getObjectAlignment());
179 | }
180 | return Math.max(size, CURRENT_JVM_INFORMATION.getMinimumObjectSize());
181 | }
182 | }
183 |
184 | private long guessArraySize(Object obj) {
185 | long size = PrimitiveType.getArraySize();
186 | int length = Array.getLength(obj);
187 | if (length != 0) {
188 | Class> arrayElementClazz = obj.getClass().getComponentType();
189 | if (arrayElementClazz.isPrimitive()) {
190 | size += length * PrimitiveType.forType(arrayElementClazz).getSize();
191 | } else {
192 | size += length * PrimitiveType.getReferenceSize();
193 | }
194 | }
195 | if ((size % CURRENT_JVM_INFORMATION.getObjectAlignment()) != 0) {
196 | size += CURRENT_JVM_INFORMATION.getObjectAlignment() - (size % CURRENT_JVM_INFORMATION.getObjectAlignment());
197 | }
198 | return Math.max(size, CURRENT_JVM_INFORMATION.getMinimumObjectSize());
199 | }
200 | }
201 |
--------------------------------------------------------------------------------
/src/main/java/org/ehcache/sizeof/impl/SizeOfAgent.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof.impl;
18 |
19 | import java.lang.instrument.Instrumentation;
20 |
21 | /**
22 | * @author Alex Snaps
23 | */
24 | public class SizeOfAgent {
25 |
26 | private static volatile Instrumentation instrumentation;
27 | private static final String NO_INSTRUMENTATION_SYSTEM_PROPERTY_NAME = "org.ehcache.sizeof.agent.instrumentationSystemProperty";
28 |
29 | public static void premain(String options, Instrumentation inst) {
30 | SizeOfAgent.instrumentation = inst;
31 | registerSystemProperty();
32 | }
33 |
34 | public static void agentmain(String options, Instrumentation inst) {
35 | SizeOfAgent.instrumentation = inst;
36 | registerSystemProperty();
37 | }
38 |
39 | private static void registerSystemProperty() {
40 | if (Boolean.getBoolean(NO_INSTRUMENTATION_SYSTEM_PROPERTY_NAME)) {
41 | System.getProperties().put(AgentLoader.INSTRUMENTATION_INSTANCE_SYSTEM_PROPERTY_NAME, instrumentation);
42 | }
43 | }
44 |
45 | public static Instrumentation getInstrumentation() {
46 | return instrumentation;
47 | }
48 |
49 | private SizeOfAgent() {
50 | //not instantiable
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/org/ehcache/sizeof/impl/UnsafeSizeOf.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof.impl;
18 |
19 | import sun.misc.Unsafe;
20 | import org.ehcache.sizeof.SizeOf;
21 | import org.ehcache.sizeof.filters.SizeOfFilter;
22 | import org.slf4j.Logger;
23 | import org.slf4j.LoggerFactory;
24 |
25 | import java.lang.reflect.Array;
26 | import java.lang.reflect.Field;
27 | import java.lang.reflect.Modifier;
28 |
29 | import static org.ehcache.sizeof.impl.JvmInformation.CURRENT_JVM_INFORMATION;
30 |
31 | /**
32 | * {@link sun.misc.Unsafe#theUnsafe} based sizeOf measurement
33 | * All constructors will throw UnsupportedOperationException if theUnsafe isn't accessible on this platform
34 | *
35 | * @author Chris Dennis
36 | */
37 | @SuppressWarnings("restriction")
38 | public class UnsafeSizeOf extends SizeOf {
39 |
40 |
41 | private static final Logger LOGGER = LoggerFactory.getLogger(UnsafeSizeOf.class);
42 |
43 | private static final Unsafe UNSAFE;
44 |
45 | static {
46 | Unsafe unsafe;
47 | try {
48 | Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
49 | unsafeField.setAccessible(true);
50 | unsafe = (Unsafe)unsafeField.get(null);
51 | } catch (Throwable t) {
52 | unsafe = null;
53 | }
54 | UNSAFE = unsafe;
55 | }
56 |
57 | /**
58 | * Builds a new SizeOf that will not filter fields and will cache reflected fields
59 | *
60 | * @throws UnsupportedOperationException If Unsafe isn't accessible
61 | * @see #UnsafeSizeOf(org.ehcache.sizeof.filters.SizeOfFilter, boolean, boolean)
62 | */
63 | public UnsafeSizeOf() throws UnsupportedOperationException {
64 | this(new PassThroughFilter());
65 | }
66 |
67 | /**
68 | * Builds a new SizeOf that will filter fields according to the provided filter and will cache reflected fields
69 | *
70 | * @param filter The filter to apply
71 | * @throws UnsupportedOperationException If Unsafe isn't accessible
72 | * @see #UnsafeSizeOf(org.ehcache.sizeof.filters.SizeOfFilter, boolean, boolean)
73 | * @see org.ehcache.sizeof.filters.SizeOfFilter
74 | */
75 | public UnsafeSizeOf(SizeOfFilter filter) throws UnsupportedOperationException {
76 | this(filter, true, true);
77 | }
78 |
79 | /**
80 | * Builds a new SizeOf that will filter fields according to the provided filter
81 | *
82 | * @param filter The filter to apply
83 | * @param caching whether to cache reflected fields
84 | * @param bypassFlyweight whether "Flyweight Objects" are to be ignored
85 | * @throws UnsupportedOperationException If Unsafe isn't accessible
86 | * @see SizeOfFilter
87 | */
88 | public UnsafeSizeOf(SizeOfFilter filter, boolean caching, boolean bypassFlyweight) throws UnsupportedOperationException {
89 | super(filter, caching, bypassFlyweight);
90 | if (UNSAFE == null) {
91 | throw new UnsupportedOperationException("sun.misc.Unsafe instance not accessible");
92 | }
93 |
94 | if (!CURRENT_JVM_INFORMATION.supportsUnsafeSizeOf()) {
95 | LOGGER.warn("UnsafeSizeOf is not always accurate on the JVM (" + CURRENT_JVM_INFORMATION.getJvmDescription() +
96 | "). Please consider enabling AgentSizeOf.");
97 | }
98 |
99 | }
100 |
101 | /**
102 | * {@inheritDoc}
103 | */
104 | @Override
105 | public long sizeOf(Object obj) {
106 | if (obj.getClass().isArray()) {
107 | Class> klazz = obj.getClass();
108 | int base = UNSAFE.arrayBaseOffset(klazz);
109 | int scale = UNSAFE.arrayIndexScale(klazz);
110 | long size = base + (scale * Array.getLength(obj));
111 | size += CURRENT_JVM_INFORMATION.getFieldOffsetAdjustment();
112 | if ((size % CURRENT_JVM_INFORMATION.getObjectAlignment()) != 0) {
113 | size += CURRENT_JVM_INFORMATION.getObjectAlignment() - (size % CURRENT_JVM_INFORMATION.getObjectAlignment());
114 | }
115 | return Math.max(CURRENT_JVM_INFORMATION.getMinimumObjectSize(), size);
116 | } else {
117 | for (Class> klazz = obj.getClass(); klazz != null; klazz = klazz.getSuperclass()) {
118 | long lastFieldOffset = -1;
119 | for (Field f : klazz.getDeclaredFields()) {
120 | if (!Modifier.isStatic(f.getModifiers())) {
121 | lastFieldOffset = Math.max(lastFieldOffset, UNSAFE.objectFieldOffset(f));
122 | }
123 | }
124 | if (lastFieldOffset > 0) {
125 | lastFieldOffset += CURRENT_JVM_INFORMATION.getFieldOffsetAdjustment();
126 | lastFieldOffset += 1;
127 | if ((lastFieldOffset % CURRENT_JVM_INFORMATION.getObjectAlignment()) != 0) {
128 | lastFieldOffset += CURRENT_JVM_INFORMATION.getObjectAlignment() -
129 | (lastFieldOffset % CURRENT_JVM_INFORMATION.getObjectAlignment());
130 | }
131 | return Math.max(CURRENT_JVM_INFORMATION.getMinimumObjectSize(), lastFieldOffset);
132 | }
133 | }
134 |
135 | long size = CURRENT_JVM_INFORMATION.getObjectHeaderSize();
136 | if ((size % CURRENT_JVM_INFORMATION.getObjectAlignment()) != 0) {
137 | size += CURRENT_JVM_INFORMATION.getObjectAlignment() - (size % CURRENT_JVM_INFORMATION.getObjectAlignment());
138 | }
139 | return Math.max(CURRENT_JVM_INFORMATION.getMinimumObjectSize(), size);
140 | }
141 | }
142 |
143 | }
144 |
--------------------------------------------------------------------------------
/src/main/java/org/ehcache/sizeof/util/WeakIdentityConcurrentMap.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof.util;
18 |
19 | import java.lang.ref.Reference;
20 | import java.lang.ref.ReferenceQueue;
21 | import java.lang.ref.WeakReference;
22 | import java.util.HashSet;
23 | import java.util.Set;
24 | import java.util.concurrent.ConcurrentHashMap;
25 | import java.util.concurrent.ConcurrentMap;
26 |
27 | /**
28 | * A poor man's implementation of a WeakIdentityConcurrentMap to hold the CacheManager associated ExecutorServices
29 | *
30 | * @param The key type
31 | * @param The value type
32 | * @author Alex Snaps
33 | */
34 | public final class WeakIdentityConcurrentMap {
35 |
36 | private final ConcurrentMap, V> map = new ConcurrentHashMap<>();
37 | private final ReferenceQueue queue = new ReferenceQueue<>();
38 |
39 | private final CleanUpTask cleanUpTask;
40 |
41 | /**
42 | * Constructor
43 | */
44 | public WeakIdentityConcurrentMap() {
45 | this(null);
46 | }
47 |
48 | /**
49 | * Constructor
50 | *
51 | * @param cleanUpTask task cleaning up references
52 | */
53 | public WeakIdentityConcurrentMap(final CleanUpTask cleanUpTask) {
54 | this.cleanUpTask = cleanUpTask;
55 | }
56 |
57 | /**
58 | * Puts into the underlying
59 | *
60 | * @param key key with which the specified value is to be associated
61 | * @param value value to be associated with the specified key
62 | * @return the previous value associated with key , or
63 | * null if there was no mapping for key .
64 | * (A null return can also indicate that the map
65 | * previously associated null with key ,
66 | * if the implementation supports null values.)
67 | */
68 | public V put(K key, V value) {
69 | cleanUp();
70 | return map.put(new IdentityWeakReference<>(key, queue), value);
71 | }
72 |
73 | /**
74 | * Remove from the underlying
75 | *
76 | * @param key key whose mapping is to be removed from the map
77 | * @return the previous value associated with key , or
78 | * null if there was no mapping for key .
79 | */
80 | public V remove(K key) {
81 | cleanUp();
82 | return map.remove(new IdentityWeakReference<>(key, queue));
83 | }
84 |
85 | /**
86 | * {@inheritDoc}
87 | */
88 | @Override
89 | public String toString() {
90 | cleanUp();
91 | return map.toString();
92 | }
93 |
94 | /**
95 | * Puts into the underlying
96 | *
97 | * @param key key with which the specified value is to be associated
98 | * @param value value to be associated with the specified key
99 | * @return the previous value associated with the specified key, or
100 | * {@code null} if there was no mapping for the key.
101 | * (A {@code null} return can also indicate that the map
102 | * previously associated {@code null} with the key,
103 | * if the implementation supports null values.)
104 | */
105 | public V putIfAbsent(K key, V value) {
106 | cleanUp();
107 | return map.putIfAbsent(new IdentityWeakReference<>(key, queue), value);
108 | }
109 |
110 | /**
111 | * @param key the key whose associated value is to be returned
112 | * @return the value to which the specified key is mapped, or
113 | * {@code null} if this map contains no mapping for the key
114 | */
115 | public V get(K key) {
116 | cleanUp();
117 | return map.get(new IdentityWeakReference<>(key));
118 | }
119 |
120 | /**
121 | *
122 | */
123 | public void cleanUp() {
124 |
125 | Reference extends K> reference;
126 | while ((reference = queue.poll()) != null) {
127 | final V value = map.remove(reference);
128 | if (cleanUpTask != null && value != null) {
129 | cleanUpTask.cleanUp(value);
130 | }
131 | }
132 | }
133 |
134 | /**
135 | * @return a set view of the keys contained in this map
136 | */
137 | public Set keySet() {
138 | cleanUp();
139 | K k;
140 | final HashSet ks = new HashSet<>();
141 | for (WeakReference weakReference : map.keySet()) {
142 | k = weakReference.get();
143 | if (k != null) {
144 | ks.add(k);
145 | }
146 | }
147 | return ks;
148 | }
149 |
150 | public boolean containsKey(final K key) {
151 | cleanUp();
152 | return map.containsKey(new IdentityWeakReference<>(key));
153 | }
154 |
155 | /**
156 | * @param
157 | */
158 | private static final class IdentityWeakReference extends WeakReference {
159 |
160 | private final int hashCode;
161 |
162 | /**
163 | * @param reference the referenced object
164 | */
165 | IdentityWeakReference(T reference) {
166 | this(reference, null);
167 | }
168 |
169 | /**
170 | * @param reference the references object
171 | * @param referenceQueue the reference queue where references are kept
172 | */
173 | IdentityWeakReference(T reference, ReferenceQueue referenceQueue) {
174 | super(reference, referenceQueue);
175 | this.hashCode = (reference == null) ? 0 : System.identityHashCode(reference);
176 | }
177 |
178 | /**
179 | * {@inheritDoc}
180 | */
181 | @Override
182 | public String toString() {
183 | return String.valueOf(get());
184 | }
185 |
186 | /**
187 | * {@inheritDoc}
188 | */
189 | @Override
190 | public boolean equals(Object o) {
191 | if (this == o) {
192 | return true;
193 | }
194 | if (!(o instanceof IdentityWeakReference>)) {
195 | return false;
196 | } else {
197 | IdentityWeakReference> wr = (IdentityWeakReference>)o;
198 | Object got = get();
199 | return (got != null && got == wr.get());
200 | }
201 | }
202 |
203 | /**
204 | * {@inheritDoc}
205 | */
206 | @Override
207 | public int hashCode() {
208 | return hashCode;
209 | }
210 | }
211 |
212 | /**
213 | * @param
214 | */
215 | public interface CleanUpTask {
216 |
217 | /**
218 | * @param object object to cleanup
219 | */
220 | void cleanUp(T object);
221 | }
222 | }
223 |
--------------------------------------------------------------------------------
/src/test/java/com/terracotta/ehcache/special/annotation/IgnoreSizeOf.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package com.terracotta.ehcache.special.annotation;
18 |
19 | import java.lang.annotation.ElementType;
20 | import java.lang.annotation.Retention;
21 | import java.lang.annotation.RetentionPolicy;
22 | import java.lang.annotation.Target;
23 |
24 | @Retention(RetentionPolicy.RUNTIME)
25 | @Target({ ElementType.FIELD, ElementType.TYPE, ElementType.PACKAGE })
26 | public @interface IgnoreSizeOf {
27 | boolean inherited() default true;
28 | }
29 |
--------------------------------------------------------------------------------
/src/test/java/com/terracotta/ehcache/special/annotation/IgnoreSizeOffff.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package com.terracotta.ehcache.special.annotation;
18 |
19 | import java.lang.annotation.ElementType;
20 | import java.lang.annotation.Retention;
21 | import java.lang.annotation.RetentionPolicy;
22 | import java.lang.annotation.Target;
23 |
24 | @Retention(RetentionPolicy.RUNTIME)
25 | @Target({ ElementType.FIELD, ElementType.TYPE, ElementType.PACKAGE })
26 | public @interface IgnoreSizeOffff {
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/test/java/com/terracotta/ehcache/special/annotation/no/inherited/IgnoreSizeOf.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package com.terracotta.ehcache.special.annotation.no.inherited;
18 |
19 | import java.lang.annotation.ElementType;
20 | import java.lang.annotation.Retention;
21 | import java.lang.annotation.RetentionPolicy;
22 | import java.lang.annotation.Target;
23 |
24 | @Retention(RetentionPolicy.RUNTIME)
25 | @Target({ ElementType.FIELD, ElementType.TYPE, ElementType.PACKAGE })
26 | public @interface IgnoreSizeOf {
27 | }
28 |
--------------------------------------------------------------------------------
/src/test/java/com/terracotta/special/annotation/IgnoreSizeOf.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package com.terracotta.special.annotation;
18 |
19 | import java.lang.annotation.ElementType;
20 | import java.lang.annotation.Retention;
21 | import java.lang.annotation.RetentionPolicy;
22 | import java.lang.annotation.Target;
23 |
24 | @Retention(RetentionPolicy.RUNTIME)
25 | @Target({ ElementType.FIELD, ElementType.TYPE, ElementType.PACKAGE })
26 | public @interface IgnoreSizeOf {
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/test/java/org/ehcache/sizeof/AbstractSizeOfTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof;
18 |
19 | import java.lang.management.ManagementFactory;
20 |
21 | import javax.management.MBeanServer;
22 | import javax.management.ObjectName;
23 | import javax.management.openmbean.CompositeData;
24 |
25 | import static org.ehcache.sizeof.impl.JvmInformation.CURRENT_JVM_INFORMATION;
26 |
27 | /**
28 | * @author Alex Snaps
29 | */
30 | abstract class AbstractSizeOfTest {
31 |
32 | protected static final boolean COMPRESSED_OOPS;
33 | protected static final boolean HOTSPOT_CMS;
34 | protected static final boolean IS_HOTSPOT;
35 | protected static final boolean IS_JROCKIT;
36 | protected static final boolean IS_IBM;
37 | protected static final boolean IS_64_BIT;
38 |
39 | static {
40 | String value = getVmOptionValue("UseCompressedOops");
41 | if (value == null) {
42 | System.err.println("Could not detect compressed-oops status assuming: false");
43 | COMPRESSED_OOPS = false;
44 | } else {
45 | COMPRESSED_OOPS = Boolean.valueOf(value);
46 | }
47 |
48 | HOTSPOT_CMS = CURRENT_JVM_INFORMATION.getMinimumObjectSize() > CURRENT_JVM_INFORMATION.getObjectAlignment();
49 |
50 | IS_64_BIT = System.getProperty("sun.arch.data.model").equals("64");
51 |
52 | IS_HOTSPOT = System.getProperty("java.vm.name", "").toLowerCase().contains("hotspot");
53 |
54 | IS_JROCKIT = System.getProperty("jrockit.version") != null ||
55 | System.getProperty("java.vm.name", "").toLowerCase().contains("jrockit");
56 |
57 | IS_IBM = System.getProperty("java.vm.name", "").contains("IBM") &&
58 | System.getProperty("java.vm.vendor").contains("IBM");
59 | }
60 |
61 | private static String getVmOptionValue(String name) {
62 | try {
63 | MBeanServer server = ManagementFactory.getPlatformMBeanServer();
64 | Object vmOption = server.invoke(ObjectName.getInstance("com.sun.management:type=HotSpotDiagnostic"), "getVMOption", new Object[] { name }, new String[] { "java.lang.String" });
65 | return (String)((CompositeData)vmOption).get("value");
66 | } catch (Throwable t) {
67 | return null;
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/test/java/org/ehcache/sizeof/ConfigurationTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof;
18 |
19 | import org.ehcache.sizeof.filters.AnnotationSizeOfFilter;
20 | import org.hamcrest.CoreMatchers;
21 | import org.junit.Test;
22 |
23 | import static org.hamcrest.CoreMatchers.is;
24 | import static org.hamcrest.CoreMatchers.sameInstance;
25 | import static org.junit.Assert.assertThat;
26 |
27 | /**
28 | * @author Alex Snaps
29 | */
30 | public class ConfigurationTest {
31 |
32 | @Test
33 | public void testBuilderBuilds() {
34 | final AnnotationSizeOfFilter filter = new AnnotationSizeOfFilter();
35 | final Configuration configuration = new Configuration.Builder().abort(true)
36 | .maxDepth(666)
37 | .silent(true)
38 | .addFilter(filter)
39 | .build();
40 | assertThat(configuration.getFilters().length, is(1));
41 | assertThat(configuration.getFilters()[0], CoreMatchers.sameInstance(filter));
42 | assertThat(configuration.isAbort(), is(true));
43 | assertThat(configuration.isSilent(), is(true));
44 | assertThat(configuration.getMaxDepth(), is(666));
45 | }
46 |
47 | @Test
48 | public void testBuilderCopies() {
49 | final AnnotationSizeOfFilter filter = new AnnotationSizeOfFilter();
50 | final Configuration template = new Configuration.Builder().abort(true)
51 | .maxDepth(666)
52 | .silent(true)
53 | .addFilter(filter)
54 | .build();
55 | final Configuration cfg = new Configuration.Builder(template).build();
56 | assertThat(cfg.getMaxDepth(), is(template.getMaxDepth()));
57 | assertThat(cfg.isSilent(), is(template.isSilent()));
58 | assertThat(cfg.isAbort(), is(template.isAbort()));
59 | assertThat(cfg.getFilters()[0], sameInstance(template.getFilters()[0]));
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/src/test/java/org/ehcache/sizeof/CrossCheckingSizeOf.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof;
18 |
19 | import org.ehcache.sizeof.filters.SizeOfFilter;
20 | import org.ehcache.sizeof.impl.AgentSizeOf;
21 | import org.ehcache.sizeof.impl.PassThroughFilter;
22 | import org.ehcache.sizeof.impl.ReflectionSizeOf;
23 | import org.ehcache.sizeof.impl.UnsafeSizeOf;
24 |
25 | import java.lang.reflect.Array;
26 | import java.util.ArrayList;
27 | import java.util.List;
28 |
29 | import static org.ehcache.sizeof.impl.JvmInformation.CURRENT_JVM_INFORMATION;
30 |
31 | /**
32 | * @author Alex Snaps
33 | */
34 | public class CrossCheckingSizeOf extends SizeOf {
35 |
36 | private final List engines;
37 |
38 | public CrossCheckingSizeOf() {
39 | this(new PassThroughFilter());
40 | }
41 |
42 | public CrossCheckingSizeOf(boolean bypassFlyweight) {
43 | this(new PassThroughFilter(), true, bypassFlyweight);
44 | }
45 |
46 | public CrossCheckingSizeOf(SizeOfFilter filter) {
47 | this(filter, true, true);
48 | }
49 |
50 | public CrossCheckingSizeOf(SizeOfFilter filter, boolean caching, boolean bypassFlyweight) {
51 | super(filter, caching, bypassFlyweight);
52 | engines = new ArrayList<>();
53 |
54 | try {
55 | engines.add(new AgentSizeOf());
56 | } catch (UnsupportedOperationException usoe) {
57 | System.err.println("Not using AgentSizeOf: " + usoe);
58 | }
59 | try {
60 | engines.add(new UnsafeSizeOf());
61 | } catch (UnsupportedOperationException usoe) {
62 | System.err.println("Not using UnsafeSizeOf: " + usoe);
63 | }
64 | if (CURRENT_JVM_INFORMATION.supportsReflectionSizeOf()) {
65 | try {
66 | engines.add(new ReflectionSizeOf());
67 | } catch (UnsupportedOperationException usoe) {
68 | System.err.println("Not using ReflectionSizeOf: " + usoe);
69 | }
70 | } else {
71 | System.err.println(CURRENT_JVM_INFORMATION.getJvmDescription() + " detected: not using ReflectionSizeOf");
72 | }
73 |
74 | if (engines.isEmpty()) {
75 | throw new AssertionError("No SizeOf engines available");
76 | }
77 | }
78 |
79 | @Override
80 | public long sizeOf(Object obj) {
81 | long[] values = new long[engines.size()];
82 | for (int i = 0; i < engines.size(); i++) {
83 | values[i] = engines.get(i).sizeOf(obj);
84 | }
85 | for (long value : values) {
86 | if (values[0] != value) {
87 | StringBuilder sb = new StringBuilder("Values do not match for ");
88 | sb.append(obj.getClass());
89 | if (obj.getClass().isArray()) {
90 | sb.append(" length:").append(Array.getLength(obj));
91 | }
92 | sb.append(" - ");
93 | for (int i = 0; i < engines.size(); i++) {
94 | sb.append(engines.get(i).getClass().getSimpleName()).append(":").append(values[i]);
95 | if (i != engines.size() - 1) {
96 | sb.append(", ");
97 | }
98 | }
99 | throw new AssertionError(sb.toString());
100 | }
101 | }
102 |
103 | return values[0];
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/test/java/org/ehcache/sizeof/CrossCheckingSizeOfIT.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof;
18 |
19 | import java.util.ArrayList;
20 | import java.util.Arrays;
21 | import java.util.Collection;
22 | import java.util.Collections;
23 | import java.util.Comparator;
24 | import java.util.HashSet;
25 | import java.util.List;
26 | import java.util.Set;
27 |
28 | import org.junit.Test;
29 | import org.objectweb.asm.ClassWriter;
30 | import org.objectweb.asm.MethodVisitor;
31 | import org.objectweb.asm.Type;
32 |
33 | import static org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
34 | import static org.objectweb.asm.ClassWriter.COMPUTE_MAXS;
35 | import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
36 | import static org.objectweb.asm.Opcodes.ALOAD;
37 | import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
38 | import static org.objectweb.asm.Opcodes.RETURN;
39 | import static org.objectweb.asm.Opcodes.V1_6;
40 |
41 | /**
42 | *
43 | * @author cdennis
44 | */
45 | public class CrossCheckingSizeOfIT {
46 |
47 | private static final Comparator> COMPARATOR = Comparator.comparing(Class::getName);
48 |
49 | private static final Collection> FIELD_TYPES = Arrays.asList(
50 | Byte.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE, Object.class);
51 |
52 | @Test
53 | public void testSingleClass() throws Exception {
54 | CrossCheckingSizeOf sizeOf = new CrossCheckingSizeOf();
55 | for (int i = 0; i <= 9; i++) {
56 | for (List> testcase : permutations(FIELD_TYPES, COMPARATOR, i)) {
57 | sizeOf.sizeOf(generateClassHierarchy(Collections.singletonList(testcase)).newInstance());
58 | }
59 | }
60 | }
61 |
62 | @Test
63 | public void testTwoClasses() throws Exception {
64 | CrossCheckingSizeOf sizeOf = new CrossCheckingSizeOf();
65 | for (int i = 0; i <= 5; i++) {
66 | for (int j = 0; j <= 5; j++) {
67 | for (List> superklazz : permutations(FIELD_TYPES, COMPARATOR, i)) {
68 | for (List> klazz : permutations(FIELD_TYPES, COMPARATOR, j)) {
69 | sizeOf.sizeOf(generateClassHierarchy(Arrays.asList(superklazz, klazz)).newInstance());
70 | }
71 | }
72 | }
73 | }
74 | }
75 |
76 | private static Set> permutations(Collection choices, Comparator super T> comparator, int count) {
77 | if (count < 0) {
78 | throw new IllegalArgumentException();
79 | } else if (count == 0) {
80 | return Collections.singleton(Collections.emptyList());
81 | } else {
82 | Set> subperms = permutations(choices, comparator, count -1);
83 | Set> perms = new HashSet<>(subperms.size() * choices.size());
84 | for (List sub : subperms) {
85 | for (T element : choices) {
86 | List p = new ArrayList<>(sub.size() + 1);
87 | p.addAll(sub);
88 | p.add(element);
89 | p.sort(comparator);
90 | perms.add(p);
91 | }
92 | }
93 | return perms;
94 | }
95 | }
96 |
97 | private static Class> generateClassHierarchy(List>> classes) {
98 | TestClassLoader loader = new TestClassLoader();
99 |
100 | String superClassDesc = "java/lang/Object";
101 | int classIndex = 0;
102 |
103 | for (List> fields : classes) {
104 | String classDesc = "A" + classIndex++;
105 | ClassWriter cw = new ClassWriter(COMPUTE_MAXS | COMPUTE_FRAMES);
106 | cw.visit(V1_6, ACC_PUBLIC, classDesc, null, superClassDesc, null);
107 | MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null);
108 | mv.visitCode();
109 | mv.visitVarInsn(ALOAD, 0);
110 | mv.visitMethodInsn(INVOKESPECIAL, superClassDesc, "", "()V", false);
111 | mv.visitInsn(RETURN);
112 | mv.visitMaxs(1, 1);
113 | mv.visitEnd();
114 |
115 | int fieldIndex = 0;
116 | for (Class> field : fields) {
117 | cw.visitField(ACC_PUBLIC, "a" + fieldIndex++, Type.getDescriptor(field), null, null);
118 | }
119 | cw.visitEnd();
120 | loader.defineClass(classDesc, cw.toByteArray());
121 | superClassDesc = classDesc;
122 | }
123 |
124 | try {
125 | return loader.loadClass("A" + (classIndex - 1));
126 | } catch (ClassNotFoundException | NoClassDefFoundError e) {
127 | throw new AssertionError(e);
128 | }
129 | }
130 |
131 | private static class TestClassLoader extends ClassLoader {
132 |
133 | public void defineClass(String name, byte[] classbytes) {
134 | defineClass(name, classbytes, 0, classbytes.length);
135 | }
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/src/test/java/org/ehcache/sizeof/FilteredSizeOfTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof;
18 |
19 | import org.ehcache.sizeof.annotations.IgnoreSizeOf;
20 | import org.ehcache.sizeof.filteredtest.AnnotationFilteredPackage;
21 | import org.ehcache.sizeof.filteredtest.custom.CustomAnnotationFilteredPackage;
22 | import org.ehcache.sizeof.filters.AnnotationSizeOfFilter;
23 | import org.junit.BeforeClass;
24 | import org.junit.Test;
25 |
26 | import static org.hamcrest.CoreMatchers.allOf;
27 | import static org.hamcrest.Matchers.greaterThan;
28 | import static org.hamcrest.Matchers.lessThan;
29 | import static org.hamcrest.core.IsEqual.equalTo;
30 | import static org.junit.Assert.assertThat;
31 |
32 | /**
33 | * @author Alex Snaps
34 | */
35 | public class FilteredSizeOfTest extends AbstractSizeOfTest {
36 | private static long deepSizeOf(SizeOf sizeOf, Object... obj) {
37 | return sizeOf.deepSizeOf(obj);
38 | }
39 |
40 | @BeforeClass
41 | public static void setup() {
42 | deepSizeOf(new CrossCheckingSizeOf(), new Object());
43 | System.out.println("Testing for a " + System.getProperty("java.version") + " JDK "
44 | + ") on a " + System.getProperty("sun.arch.data.model") + "-bit VM "
45 | + "(compressed-oops: " + COMPRESSED_OOPS
46 | + ", Hotspot CMS: " + HOTSPOT_CMS
47 | + ")");
48 | }
49 |
50 | @Test
51 | public void testAnnotationFiltering() throws Exception {
52 | SizeOf sizeOf = new CrossCheckingSizeOf(new AnnotationSizeOfFilter());
53 |
54 | assertThat(deepSizeOf(sizeOf, new AnnotationFilteredField()), allOf(greaterThan(128L), lessThan(16 * 1024L)));
55 | assertThat(deepSizeOf(sizeOf, new AnnotationFilteredClass()), equalTo(0L));
56 | assertThat(deepSizeOf(sizeOf, new AnnotationFilteredPackage()), equalTo(0L));
57 |
58 | assertThat(deepSizeOf(sizeOf, new AnnotationFilteredFieldSubclass()), allOf(greaterThan(128L), lessThan(16 * 1024L)));
59 |
60 | long emptyReferrerSize = deepSizeOf(sizeOf, new Referrer(null));
61 | assertThat(deepSizeOf(sizeOf, new Referrer(new AnnotationFilteredClass())), equalTo(emptyReferrerSize));
62 | assertThat(deepSizeOf(sizeOf, new Referrer(new AnnotationFilteredPackage())), equalTo(emptyReferrerSize));
63 | assertThat(deepSizeOf(sizeOf, new Parent()), equalTo(0L));
64 | assertThat(deepSizeOf(sizeOf, new Child()), equalTo(0L));
65 | assertThat(deepSizeOf(sizeOf, new ChildChild()), equalTo(0L));
66 | assertThat(deepSizeOf(sizeOf, new ChildChildChild()), equalTo(0L));
67 | }
68 |
69 | @Test
70 | @SuppressWarnings("unchecked")
71 | public void testCustomAnnotationFiltering() throws Exception {
72 | SizeOf sizeOf = new CrossCheckingSizeOf(new AnnotationSizeOfFilter());
73 | assertThat(deepSizeOf(sizeOf, new MatchingPatternOrNotAnnotationFilteredField()), allOf(greaterThan(128L), lessThan(16 * 1024L)));
74 | assertThat(deepSizeOf(sizeOf, new MatchingPatternAnnotation()), equalTo(0L));
75 | assertThat(deepSizeOf(sizeOf, new MatchingPatternAnnotationChild()), equalTo(0L));
76 | assertThat(deepSizeOf(sizeOf, new MatchingPatternAnnotationNoInheritedChild()), allOf(greaterThan(4L)));
77 | assertThat(deepSizeOf(sizeOf, new NonMatchingPatternAnnotation1()), allOf(greaterThan(4L)));
78 | assertThat(deepSizeOf(sizeOf, new NonMatchingPatternAnnotation2()), allOf(greaterThan(4L)));
79 | assertThat(deepSizeOf(sizeOf, new CustomAnnotationFilteredPackage()), equalTo(0L));
80 | }
81 |
82 | @Test(expected = IllegalStateException.class)
83 | public void testNotPossibleToHaveTwoIgnoreSizeOfAnnotations() throws Exception {
84 | SizeOf sizeOf = new CrossCheckingSizeOf(new AnnotationSizeOfFilter());
85 | deepSizeOf(sizeOf, new AnnotatedTwice());
86 | }
87 |
88 |
89 | public static class AnnotationFilteredField {
90 |
91 | @IgnoreSizeOf
92 | private final byte[] bigArray = new byte[16 * 1024];
93 | private final byte[] smallArray = new byte[128];
94 | }
95 |
96 | public static class AnnotationFilteredFieldSubclass extends AnnotationFilteredField {
97 | }
98 |
99 | @IgnoreSizeOf
100 | public static class AnnotationFilteredClass {
101 |
102 | private final byte[] bigArray = new byte[16 * 1024];
103 | }
104 |
105 | @IgnoreSizeOf(inherited = true)
106 | public static class Parent {
107 | }
108 |
109 | public static class Child extends Parent {
110 | }
111 |
112 | @IgnoreSizeOf
113 | public static class ChildChild extends Child {
114 | }
115 |
116 | public static class ChildChildChild extends ChildChild {
117 | }
118 |
119 | @com.terracotta.ehcache.special.annotation.IgnoreSizeOf
120 | public static class MatchingPatternAnnotation {
121 | }
122 |
123 | public static class MatchingPatternAnnotationChild extends MatchingPatternAnnotation {
124 | }
125 |
126 | @com.terracotta.ehcache.special.annotation.no.inherited.IgnoreSizeOf
127 | public static class MatchingPatternAnnotationNoInherited {
128 | }
129 |
130 | public static class MatchingPatternAnnotationNoInheritedChild extends MatchingPatternAnnotationNoInherited {
131 | }
132 |
133 | @com.terracotta.ehcache.special.annotation.IgnoreSizeOffff
134 | public static class NonMatchingPatternAnnotation1 {
135 | }
136 |
137 | @com.terracotta.special.annotation.IgnoreSizeOf
138 | public static class NonMatchingPatternAnnotation2 {
139 | }
140 |
141 | @com.terracotta.ehcache.special.annotation.IgnoreSizeOf
142 | @IgnoreSizeOf
143 | public static class AnnotatedTwice {
144 | }
145 |
146 | public static class MatchingPatternOrNotAnnotationFilteredField {
147 | @com.terracotta.ehcache.special.annotation.IgnoreSizeOf
148 | private final byte[] matchingBigArray = new byte[16 * 1024];
149 | @com.terracotta.special.annotation.IgnoreSizeOf
150 | private final byte[] nonMatchingSmallArray = new byte[128];
151 | }
152 |
153 | public static class ResourceFilteredField {
154 |
155 | private final byte[] bigArray = new byte[16 * 1024];
156 | private final byte[] smallArray = new byte[128];
157 | }
158 |
159 | public static class ResourceFilteredFieldSubclass extends ResourceFilteredField {
160 | }
161 |
162 | public static class ResourceFilteredClass {
163 |
164 | private final byte[] bigArray = new byte[6 * 1024];
165 | }
166 |
167 | public static class Referrer {
168 |
169 | private final Object reference;
170 |
171 | public Referrer(Object obj) {
172 | this.reference = obj;
173 | }
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/src/test/java/org/ehcache/sizeof/ObjectGraphWalkerTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof;
18 |
19 | import org.ehcache.sizeof.filters.SizeOfFilter;
20 | import org.ehcache.sizeof.impl.PassThroughFilter;
21 | import org.junit.Test;
22 |
23 | import java.lang.reflect.Field;
24 | import java.util.Collection;
25 | import java.util.HashMap;
26 | import java.util.Map;
27 | import java.util.concurrent.atomic.AtomicInteger;
28 | import java.util.concurrent.locks.ReentrantReadWriteLock;
29 |
30 | import static org.hamcrest.CoreMatchers.is;
31 | import static org.hamcrest.CoreMatchers.sameInstance;
32 | import static org.hamcrest.core.IsNull.nullValue;
33 | import static org.junit.Assert.assertThat;
34 | import static org.junit.Assert.fail;
35 |
36 | /**
37 | * @author Alex Snaps
38 | */
39 | public class ObjectGraphWalkerTest {
40 |
41 | @Test
42 | public void testWalksAGraph() {
43 |
44 | final Map map = new HashMap<>();
45 |
46 | ObjectGraphWalker walker = new ObjectGraphWalker(
47 | new ObjectGraphWalker.Visitor() {
48 | public long visit(final Object object) {
49 | increment(object.getClass().getName());
50 | return 1;
51 | }
52 |
53 | public void increment(String value) {
54 | if (value != null) {
55 | Long previousValue = map.get(value);
56 | if (previousValue == null) {
57 | previousValue = 0L;
58 | }
59 | map.put(value, ++previousValue);
60 | }
61 | }
62 | }, new SizeOfFilter() {
63 |
64 | /**
65 | * {@inheritDoc}
66 | */
67 | public Collection filterFields(Class> klazz, Collection fields) {
68 | return fields;
69 | }
70 |
71 | /**
72 | * {@inheritDoc}
73 | */
74 | public boolean filterClass(Class> klazz) {
75 | return true;
76 | }
77 | },
78 | true);
79 |
80 | assertThat(walker.walk(new ReentrantReadWriteLock()), is(5L));
81 | assertThat(map.remove("java.util.concurrent.locks.ReentrantReadWriteLock$Sync$ThreadLocalHoldCounter"), is(1L));
82 |
83 | assertThat(map.remove(ReentrantReadWriteLock.class.getName()), is(1L));
84 | assertThat(map.remove("java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync"), is(1L));
85 | assertThat(map.remove(ReentrantReadWriteLock.ReadLock.class.getName()), is(1L));
86 | assertThat(map.remove(ReentrantReadWriteLock.WriteLock.class.getName()), is(1L));
87 | assertThat(map.isEmpty(), is(true));
88 |
89 | assertThat(walker.walk(new SomeInnerClass()), is(14L));
90 | assertThat(map.remove("java.util.concurrent.locks.ReentrantReadWriteLock$Sync$ThreadLocalHoldCounter"), is(1L));
91 |
92 | assertThat(map.remove(SomeInnerClass.class.getName()), is(1L));
93 | assertThat(map.remove(this.getClass().getName()), is(1L));
94 | assertThat(map.remove(Object.class.getName()), is(5L));
95 | assertThat(map.remove(ReentrantReadWriteLock.class.getName()), is(1L));
96 | assertThat(map.remove("java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync"), is(1L));
97 | assertThat(map.remove(ReentrantReadWriteLock.ReadLock.class.getName()), is(1L));
98 | assertThat(map.remove(ReentrantReadWriteLock.WriteLock.class.getName()), is(1L));
99 | assertThat(map.remove(Object[].class.getName()), is(1L));
100 | // auto-boxed '0' is a flyweight - it doesn't get walked
101 | assertThat(map.remove(Integer.class.getName()), nullValue());
102 | assertThat(map.remove(int[].class.getName()), is(1L));
103 | assertThat(map.isEmpty(), is(true));
104 |
105 | assertThat(walker.walk((Object)null), is(0L));
106 | assertThat(walker.walk(), is(0L));
107 | }
108 |
109 | public class SomeInnerClass {
110 |
111 | private int value;
112 | private Object one;
113 | private final Object two = new Object();
114 | private final Object three = new Object();
115 | private final Object four = new ReentrantReadWriteLock();
116 | private final Object[] anArray = new Object[] { new Object(), new Object(), new Object(), one, two, two, three, four, value };
117 | private final int[] anIntArray = new int[] { 1, 2, 1300 };
118 |
119 | }
120 |
121 | @Test
122 | public void testUsingListenerToLimit() {
123 |
124 | final IllegalStateException illegalArgumentException = new IllegalStateException();
125 | final int maxDepth = 2;
126 |
127 | final AtomicInteger visited = new AtomicInteger();
128 | ObjectGraphWalker walker = new ObjectGraphWalker(object -> {
129 | visited.incrementAndGet();
130 | return -1;
131 | }, new PassThroughFilter(), true);
132 |
133 | final AtomicInteger counter = new AtomicInteger();
134 | try {
135 | walker.walk((object, size) -> {
136 | if (counter.incrementAndGet() >= maxDepth) {
137 | throw illegalArgumentException;
138 | }
139 | assertThat(size, is(-1L));
140 | }, new Object(), new Object(), new Object(), new Object());
141 | fail();
142 | } catch (IllegalStateException e) {
143 | assertThat(e, sameInstance(illegalArgumentException));
144 | }
145 | assertThat(visited.get(), is(maxDepth));
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/src/test/java/org/ehcache/sizeof/SizeOfFilterSourceTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof;
18 |
19 | import org.ehcache.sizeof.filters.AnnotationSizeOfFilter;
20 | import org.junit.Test;
21 |
22 | import java.io.IOException;
23 | import java.net.URL;
24 | import java.security.SecureClassLoader;
25 | import java.util.Enumeration;
26 |
27 | import static org.hamcrest.CoreMatchers.instanceOf;
28 | import static org.hamcrest.CoreMatchers.is;
29 | import static org.junit.Assert.assertThat;
30 |
31 | /**
32 | * @author Alex Snaps
33 | */
34 | public class SizeOfFilterSourceTest {
35 |
36 | @Test
37 | public void testContainsAnnotationFilterWhenConfigured() {
38 | SizeOfFilterSource source = new SizeOfFilterSource(true);
39 | assertThat(source.getFilters().length, is(2));
40 | assertThat(source.getFilters()[1], instanceOf(AnnotationSizeOfFilter.class));
41 | }
42 |
43 | @Test
44 | public void testStartsEmptyWhenConfigured() {
45 | SizeOfFilterSource source = new SizeOfFilterSource(false);
46 | assertThat(source.getFilters().length, is(1));
47 | }
48 |
49 | @Test
50 | public void testAppliesMutators() {
51 | SizeOfFilterSource source = new SizeOfFilterSource(false);
52 | assertThat(source.getFilters().length, is(1));
53 | source.applyMutators(new CheatingClassLoader());
54 | assertThat(source.getFilters().length, is(1));
55 | assertThat(source.getFilters()[0].filterClass(TestMutator.class), is(false));
56 | assertThat(source.getFilters()[0].filterClass(String.class), is(true));
57 | }
58 |
59 | private static class CheatingClassLoader extends SecureClassLoader {
60 |
61 | public CheatingClassLoader() {
62 | super(SizeOfFilterSourceTest.class.getClassLoader());
63 | }
64 |
65 | @Override
66 | public Enumeration getResources(final String name) throws IOException {
67 | final String className = FilterConfigurator.class.getName();
68 | if (name.equals("META-INF/services/" + className)) {
69 | return super.getResources("services/" + className + ".txt");
70 | }
71 | return super.getResources(name);
72 | }
73 | }
74 |
75 | public static final class TestMutator implements FilterConfigurator {
76 |
77 | @Override
78 | public void configure(final Filter ehcacheFilter) {
79 | ehcacheFilter.ignoreInstancesOf(TestMutator.class, true);
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/test/java/org/ehcache/sizeof/SizeOfTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof;
18 |
19 | import org.ehcache.sizeof.impl.JvmInformation;
20 | import org.junit.Assert;
21 | import org.junit.Assume;
22 | import org.junit.BeforeClass;
23 | import org.junit.Test;
24 |
25 | import java.math.BigDecimal;
26 | import java.math.BigInteger;
27 | import java.math.MathContext;
28 | import java.net.Proxy;
29 | import java.nio.charset.CodingErrorAction;
30 | import java.util.ArrayList;
31 | import java.util.Collection;
32 | import java.util.Collections;
33 | import java.util.LinkedList;
34 | import java.util.List;
35 | import java.util.Locale;
36 | import java.util.concurrent.TimeUnit;
37 | import java.util.concurrent.atomic.AtomicLong;
38 | import java.util.concurrent.locks.ReentrantReadWriteLock;
39 | import java.util.logging.Logger;
40 |
41 | import javax.xml.datatype.DatatypeConstants;
42 |
43 | import static org.ehcache.sizeof.impl.JvmInformation.CURRENT_JVM_INFORMATION;
44 | import static org.ehcache.sizeof.impl.JvmInformation.UNKNOWN_32_BIT;
45 | import static org.ehcache.sizeof.impl.JvmInformation.UNKNOWN_64_BIT;
46 | import static org.hamcrest.CoreMatchers.is;
47 | import static org.hamcrest.core.IsNot.not;
48 | import static org.hamcrest.core.StringContains.containsString;
49 | import static org.junit.Assert.assertTrue;
50 | import static org.junit.Assume.assumeThat;
51 |
52 | /**
53 | * @author Alex Snaps
54 | */
55 | public class SizeOfTest extends AbstractSizeOfTest {
56 | public Object[] container;
57 |
58 | private static long deepSizeOf(SizeOf sizeOf, Object... obj) {
59 | return sizeOf.deepSizeOf(obj);
60 | }
61 |
62 | @BeforeClass
63 | public static void setup() {
64 | System.err.println("java.vm.name:\t" + System.getProperty("java.vm.name", ""));
65 | System.err.println("java.vm.vendor:\t" + System.getProperty("java.vm.vendor", ""));
66 | assumeThat(System.getProperty("os.name"), not(containsString("AIX")));
67 | deepSizeOf(new CrossCheckingSizeOf(), (Object) null);
68 | System.err.println("JVM identified as: " + JvmInformation.CURRENT_JVM_INFORMATION);
69 | if (JvmInformation.CURRENT_JVM_INFORMATION == UNKNOWN_64_BIT || JvmInformation.CURRENT_JVM_INFORMATION == UNKNOWN_32_BIT) {
70 | System.getProperties().list(System.err);
71 | }
72 | }
73 |
74 | private final Collection sizeOfFailures = new LinkedList<>();
75 |
76 | @Test
77 | public void testCreatesSizeOfEngine() {
78 | assertTrue(SizeOf.newInstance().sizeOf(new Object()) > 0);
79 | }
80 |
81 | @Test
82 | public void testSizeOfFlyweight() throws Exception {
83 | SizeOf sizeOf = new CrossCheckingSizeOf(false);
84 | Assert.assertThat(deepSizeOf(sizeOf, 42), is(not(0L)));
85 | }
86 |
87 | @Test
88 | public void testSizeOf() throws Exception {
89 | Assume.assumeThat(CURRENT_JVM_INFORMATION.getMinimumObjectSize(), is(CURRENT_JVM_INFORMATION.getObjectAlignment()));
90 |
91 | SizeOf sizeOf = new CrossCheckingSizeOf();
92 | Assert.assertThat(deepSizeOf(sizeOf, TimeUnit.SECONDS), is(0L));
93 | Assert.assertThat(deepSizeOf(sizeOf, Object.class), is(0L));
94 | Assert.assertThat(deepSizeOf(sizeOf, 1), is(0L));
95 | Assert.assertThat(deepSizeOf(sizeOf, BigInteger.ZERO), is(0L));
96 | Assert.assertThat(deepSizeOf(sizeOf, BigDecimal.ZERO), is(0L));
97 | Assert.assertThat(deepSizeOf(sizeOf, MathContext.UNLIMITED), is(0L));
98 | Assert.assertThat(deepSizeOf(sizeOf, Locale.ENGLISH), is(0L));
99 | Assert.assertThat(deepSizeOf(sizeOf, Logger.getGlobal()), is(0L));
100 | Assert.assertThat(deepSizeOf(sizeOf, Collections.EMPTY_SET), is(0L));
101 | Assert.assertThat(deepSizeOf(sizeOf, Collections.EMPTY_LIST), is(0L));
102 | Assert.assertThat(deepSizeOf(sizeOf, Collections.EMPTY_MAP), is(0L));
103 | Assert.assertThat(deepSizeOf(sizeOf, String.CASE_INSENSITIVE_ORDER), is(0L));
104 | Assert.assertThat(deepSizeOf(sizeOf, System.err), is(0L));
105 | Assert.assertThat(deepSizeOf(sizeOf, Proxy.NO_PROXY), is(0L));
106 | Assert.assertThat(deepSizeOf(sizeOf, CodingErrorAction.REPORT), is(0L));
107 | Assert.assertThat(deepSizeOf(sizeOf, DatatypeConstants.DAYS), is(0L));
108 | Assert.assertThat(deepSizeOf(sizeOf, DatatypeConstants.TIME), is(0L));
109 |
110 | assertThat(sizeOf.sizeOf(new Object()), "sizeOf(new Object())");
111 | assertThat(sizeOf.sizeOf(1), "sizeOf(new Integer(1))");
112 | assertThat(sizeOf.sizeOf(1000), "sizeOf(1000)");
113 | assertThat(deepSizeOf(sizeOf, new SomeClass(false)), "deepSizeOf(new SomeClass(false))");
114 | assertThat(deepSizeOf(sizeOf, new SomeClass(true)), "deepSizeOf(new SomeClass(true))");
115 | assertThat(sizeOf.sizeOf(new Object[] { }), "sizeOf(new Object[] { })");
116 | assertThat(sizeOf.sizeOf(new Object[] { new Object(), new Object(), new Object(), new Object() }), "sizeOf(new Object[] { new Object(), new Object(), new Object(), new Object() })");
117 | assertThat(sizeOf.sizeOf(new int[] { }), "sizeOf(new int[] { })");
118 | assertThat(sizeOf.sizeOf(new int[] { 987654, 876543, 765432, 654321 }), "sizeOf(new int[] { 987654, 876543, 765432, 654321 })");
119 | assertThat(deepSizeOf(sizeOf, new Pair(null, null)), "deepSizeOf(new Pair(null, null))");
120 | assertThat(deepSizeOf(sizeOf, new Pair(new Object(), null)), "deepSizeOf(new Pair(new Object(), null))");
121 | assertThat(deepSizeOf(sizeOf, new Pair(new Object(), new Object())), "deepSizeOf(new Pair(new Object(), new Object()))");
122 | assertThat(deepSizeOf(sizeOf, new ReentrantReadWriteLock()), "deepSizeOf(new ReentrantReadWriteLock())");
123 |
124 | if (!sizeOfFailures.isEmpty()) {
125 | StringBuilder sb = new StringBuilder();
126 | for (AssertionError e : sizeOfFailures) {
127 | sb.append(e.toString()).append('\n');
128 | }
129 | Assert.fail(sb.toString());
130 | }
131 |
132 | List list1 = new ArrayList<>();
133 | List list2 = new ArrayList<>();
134 |
135 | Object someInstance = new Object();
136 | list1.add(someInstance);
137 | list2.add(someInstance);
138 |
139 | Assert.assertThat(deepSizeOf(sizeOf, list1), is(deepSizeOf(sizeOf, list2)));
140 | Assert.assertThat(deepSizeOf(sizeOf, list1, list2) < (deepSizeOf(sizeOf, list1) + deepSizeOf(sizeOf, list2)), is(true));
141 | list2.add(new Object());
142 | Assert.assertThat(deepSizeOf(sizeOf, list2) > deepSizeOf(sizeOf, list1), is(true));
143 | }
144 |
145 | private void assertThat(Long size, String expression) {
146 | try {
147 | Assert.assertThat(expression, size, is(SizeOfTestValues.get(expression)));
148 | } catch (AssertionError e) {
149 | sizeOfFailures.add(e);
150 | }
151 | }
152 |
153 | @Test
154 | public void testOnHeapConsumption() throws Exception {
155 | SizeOf sizeOf = new CrossCheckingSizeOf();
156 |
157 | int size = 80000;
158 | int perfectMatches = 0;
159 | for (int j = 0; j < 5; j++) {
160 | container = new Object[size];
161 | long usedBefore = measureMemoryUse();
162 | for (int i = 0; i < size; i++) {
163 | container[i] = new EvilPair(new Object(), new SomeClass(i % 2 == 0));
164 | }
165 |
166 | long mem = 0;
167 | for (Object s : container) {
168 | mem += deepSizeOf(sizeOf, s);
169 | }
170 |
171 | long used = measureMemoryUse() - usedBefore;
172 | float percentage = 1 - (mem / (float)used);
173 | System.err.println("Run # " + (j + 1) + ": Deviation of " + String.format("%.3f", percentage * -100) +
174 | "%\n" + used +
175 | " bytes are actually being used, while we believe " + mem + " are");
176 | if (j > 1) {
177 | Assert.assertThat("Run # " + (j + 1) + ": Deviation of " + String.format("%.3f", percentage * -100) +
178 | "% was above the +/-1.5% delta threshold \n" + used +
179 | " bytes are actually being used, while we believe " + mem + " are (" +
180 | (used - mem) / size + ")",
181 | Math.abs(percentage) < .015f, is(true));
182 | }
183 | if (used == mem && ++perfectMatches > 1) {
184 | System.err.println("Two perfect matches, that's good enough... bye y'all!");
185 | break;
186 | }
187 | }
188 | }
189 |
190 | private long measureMemoryUse() throws InterruptedException {
191 | long total;
192 | long freeAfter;
193 | long freeBefore;
194 | Runtime runtime = Runtime.getRuntime();
195 | do {
196 | total = runtime.totalMemory();
197 | freeBefore = runtime.freeMemory();
198 | System.gc();
199 | Thread.sleep(100);
200 | freeAfter = runtime.freeMemory();
201 | } while (total != runtime.totalMemory() || freeAfter > freeBefore);
202 | return total - freeAfter;
203 | }
204 |
205 | public static class SomeClass {
206 |
207 | public Object ref;
208 |
209 | public SomeClass(final boolean init) {
210 | if (init) {
211 | ref = new Object();
212 | }
213 | }
214 | }
215 |
216 | public static class Pair {
217 | private final Object one;
218 | private final Object two;
219 |
220 | public Pair(final Object one, final Object two) {
221 | this.one = one;
222 | this.two = two;
223 | }
224 | }
225 |
226 | public static final class Stupid {
227 |
228 | public static class internal {
229 | private int someValue;
230 | private long otherValue;
231 | }
232 |
233 | internal internalVar = new internal();
234 | int someValue;
235 | long someOther;
236 | long otherValue;
237 | boolean bool;
238 | }
239 |
240 | public static final class EvilPair extends Pair {
241 |
242 | private static final AtomicLong counter = new AtomicLong(Long.MIN_VALUE);
243 |
244 | private final Object oneHidden;
245 | private final Object twoHidden;
246 | private final Object copyOne;
247 | private final Object copyTwo;
248 | private final long instanceNumber;
249 |
250 | private EvilPair(final Object one, final Object two) {
251 | super(one, two);
252 | instanceNumber = counter.getAndIncrement();
253 | if (instanceNumber % 4 == 0) {
254 | oneHidden = new Object();
255 | twoHidden = new Object();
256 | } else {
257 | oneHidden = null;
258 | twoHidden = null;
259 | }
260 | this.copyOne = one;
261 | this.copyTwo = two;
262 | }
263 | }
264 | }
265 |
--------------------------------------------------------------------------------
/src/test/java/org/ehcache/sizeof/SizeOfTestValues.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof;
18 |
19 | import org.ehcache.sizeof.impl.JvmInformation;
20 | import org.junit.Assert;
21 |
22 | import java.util.Collections;
23 | import java.util.EnumMap;
24 | import java.util.HashMap;
25 | import java.util.Map;
26 |
27 | import static org.hamcrest.Matchers.hasKey;
28 |
29 | /**
30 | * @author Alex Snaps
31 | */
32 | public class SizeOfTestValues {
33 | private static final Map> CORRECT_SIZES = new EnumMap<>(JvmInformation.class);
34 |
35 | static {
36 | Map hotspot32Bit = new HashMap<>();
37 | hotspot32Bit.put("sizeOf(new Object())", 8L);
38 | hotspot32Bit.put("sizeOf(new Integer(1))", 16L);
39 | hotspot32Bit.put("sizeOf(1000)", 16L);
40 | hotspot32Bit.put("deepSizeOf(new SomeClass(false))", 16L);
41 | hotspot32Bit.put("deepSizeOf(new SomeClass(true))", 24L);
42 | hotspot32Bit.put("sizeOf(new Object[] { })", 16L);
43 | hotspot32Bit.put("sizeOf(new Object[] { new Object(), new Object(), new Object(), new Object() })", 32L);
44 | hotspot32Bit.put("sizeOf(new int[] { })", 16L);
45 | hotspot32Bit.put("sizeOf(new int[] { 987654, 876543, 765432, 654321 })", 32L);
46 | hotspot32Bit.put("deepSizeOf(new Pair(null, null))", 16L);
47 | hotspot32Bit.put("deepSizeOf(new Pair(new Object(), null))", 24L);
48 | hotspot32Bit.put("deepSizeOf(new Pair(new Object(), new Object()))", 32L);
49 | hotspot32Bit.put("deepSizeOf(new ReentrantReadWriteLock())", 112L);
50 |
51 | CORRECT_SIZES.put(JvmInformation.HOTSPOT_32_BIT, hotspot32Bit);
52 | CORRECT_SIZES.put(JvmInformation.OPENJDK_32_BIT, hotspot32Bit);
53 |
54 | Map hotspot32BitWithConcurrentMarkAndSweep = new HashMap<>();
55 | hotspot32BitWithConcurrentMarkAndSweep.put("sizeOf(new Object())", 16L);
56 | hotspot32BitWithConcurrentMarkAndSweep.put("sizeOf(new Integer(1))", 16L);
57 | hotspot32BitWithConcurrentMarkAndSweep.put("sizeOf(1000)", 16L);
58 | hotspot32BitWithConcurrentMarkAndSweep.put("deepSizeOf(new SomeClass(false))", 16L);
59 | hotspot32BitWithConcurrentMarkAndSweep.put("deepSizeOf(new SomeClass(true))", 24L);
60 | hotspot32BitWithConcurrentMarkAndSweep.put("sizeOf(new Object[] { })", 16L);
61 | hotspot32BitWithConcurrentMarkAndSweep.put("sizeOf(new Object[] { new Object(), new Object(), new Object(), new Object() })", 32L);
62 | hotspot32BitWithConcurrentMarkAndSweep.put("sizeOf(new int[] { })", 16L);
63 | hotspot32BitWithConcurrentMarkAndSweep.put("sizeOf(new int[] { 987654, 876543, 765432, 654321 })", 32L);
64 | hotspot32BitWithConcurrentMarkAndSweep.put("deepSizeOf(new Pair(null, null))", 16L);
65 | hotspot32BitWithConcurrentMarkAndSweep.put("deepSizeOf(new Pair(new Object(), null))", 24L);
66 | hotspot32BitWithConcurrentMarkAndSweep.put("deepSizeOf(new Pair(new Object(), new Object()))", 32L);
67 | hotspot32BitWithConcurrentMarkAndSweep.put("deepSizeOf(new ReentrantReadWriteLock())", 112L);
68 |
69 | CORRECT_SIZES.put(JvmInformation.HOTSPOT_32_BIT_WITH_CONCURRENT_MARK_AND_SWEEP, hotspot32BitWithConcurrentMarkAndSweep);
70 | CORRECT_SIZES.put(JvmInformation.OPENJDK_32_BIT_WITH_CONCURRENT_MARK_AND_SWEEP, hotspot32BitWithConcurrentMarkAndSweep);
71 |
72 | Map hotspot64Bit = new HashMap<>();
73 | hotspot64Bit.put("sizeOf(new Object())", 16L);
74 | hotspot64Bit.put("sizeOf(new Integer(1))", 24L);
75 | hotspot64Bit.put("sizeOf(1000)", 24L);
76 | hotspot64Bit.put("deepSizeOf(new SomeClass(false))", 24L);
77 | hotspot64Bit.put("deepSizeOf(new SomeClass(true))", 40L);
78 | hotspot64Bit.put("sizeOf(new Object[] { })", 24L);
79 | hotspot64Bit.put("sizeOf(new Object[] { new Object(), new Object(), new Object(), new Object() })", 56L);
80 | hotspot64Bit.put("sizeOf(new int[] { })", 24L);
81 | hotspot64Bit.put("sizeOf(new int[] { 987654, 876543, 765432, 654321 })", 40L);
82 | hotspot64Bit.put("deepSizeOf(new Pair(null, null))", 32L);
83 | hotspot64Bit.put("deepSizeOf(new Pair(new Object(), null))", 48L);
84 | hotspot64Bit.put("deepSizeOf(new Pair(new Object(), new Object()))", 64L);
85 | hotspot64Bit.put("deepSizeOf(new ReentrantReadWriteLock())", 192L);
86 |
87 | CORRECT_SIZES.put(JvmInformation.HOTSPOT_64_BIT, hotspot64Bit);
88 | CORRECT_SIZES.put(JvmInformation.OPENJDK_64_BIT, hotspot64Bit);
89 |
90 | Map hotspot64BitWithCMS = new HashMap<>();
91 | hotspot64BitWithCMS.put("sizeOf(new Object())", 24L);
92 | hotspot64BitWithCMS.put("sizeOf(new Integer(1))", 24L);
93 | hotspot64BitWithCMS.put("sizeOf(1000)", 24L);
94 | hotspot64BitWithCMS.put("deepSizeOf(new SomeClass(false))", 24L);
95 | hotspot64BitWithCMS.put("deepSizeOf(new SomeClass(true))", 40L);
96 | hotspot64BitWithCMS.put("sizeOf(new Object[] { })", 24L);
97 | hotspot64BitWithCMS.put("sizeOf(new Object[] { new Object(), new Object(), new Object(), new Object() })", 40L);
98 | hotspot64BitWithCMS.put("sizeOf(new int[] { })", 24L);
99 | hotspot64BitWithCMS.put("sizeOf(new int[] { 987654, 876543, 765432, 654321 })", 40L);
100 | hotspot64BitWithCMS.put("deepSizeOf(new Pair(null, null))", 32L);
101 | hotspot64BitWithCMS.put("deepSizeOf(new Pair(new Object(), null))", 48L);
102 | hotspot64BitWithCMS.put("deepSizeOf(new Pair(new Object(), new Object()))", 64L);
103 | hotspot64BitWithCMS.put("deepSizeOf(new ReentrantReadWriteLock())", 192L);
104 |
105 | CORRECT_SIZES.put(JvmInformation.HOTSPOT_64_BIT_WITH_CONCURRENT_MARK_AND_SWEEP, hotspot64BitWithCMS);
106 | CORRECT_SIZES.put(JvmInformation.OPENJDK_64_BIT_WITH_CONCURRENT_MARK_AND_SWEEP, hotspot64BitWithCMS);
107 |
108 | Map hotspot64BitWithCompressedOops = new HashMap<>();
109 | hotspot64BitWithCompressedOops.put("sizeOf(new Object())", 16L);
110 | hotspot64BitWithCompressedOops.put("sizeOf(new Integer(1))", 16L);
111 | hotspot64BitWithCompressedOops.put("sizeOf(1000)", 16L);
112 | hotspot64BitWithCompressedOops.put("deepSizeOf(new SomeClass(false))", 16L);
113 | hotspot64BitWithCompressedOops.put("deepSizeOf(new SomeClass(true))", 32L);
114 | hotspot64BitWithCompressedOops.put("sizeOf(new Object[] { })", 16L);
115 | hotspot64BitWithCompressedOops.put("sizeOf(new Object[] { new Object(), new Object(), new Object(), new Object() })", 32L);
116 | hotspot64BitWithCompressedOops.put("sizeOf(new int[] { })", 16L);
117 | hotspot64BitWithCompressedOops.put("sizeOf(new int[] { 987654, 876543, 765432, 654321 })", 32L);
118 | hotspot64BitWithCompressedOops.put("deepSizeOf(new Pair(null, null))", 24L);
119 | hotspot64BitWithCompressedOops.put("deepSizeOf(new Pair(new Object(), null))", 40L);
120 | hotspot64BitWithCompressedOops.put("deepSizeOf(new Pair(new Object(), new Object()))", 56L);
121 | hotspot64BitWithCompressedOops.put("deepSizeOf(new ReentrantReadWriteLock())", 120L);
122 |
123 | CORRECT_SIZES.put(JvmInformation.HOTSPOT_64_BIT_WITH_COMPRESSED_OOPS, hotspot64BitWithCompressedOops);
124 | CORRECT_SIZES.put(JvmInformation.OPENJDK_64_BIT_WITH_COMPRESSED_OOPS, hotspot64BitWithCompressedOops);
125 |
126 | Map hotspot64BitWithCompressedOopsAndCMS = new HashMap<>();
127 | hotspot64BitWithCompressedOopsAndCMS.put("sizeOf(new Object())", 24L);
128 | hotspot64BitWithCompressedOopsAndCMS.put("sizeOf(new Integer(1))", 24L);
129 | hotspot64BitWithCompressedOopsAndCMS.put("sizeOf(1000)", 24L);
130 | hotspot64BitWithCompressedOopsAndCMS.put("deepSizeOf(new SomeClass(false))", 24L);
131 | hotspot64BitWithCompressedOopsAndCMS.put("deepSizeOf(new SomeClass(true))", 32L);
132 | hotspot64BitWithCompressedOopsAndCMS.put("sizeOf(new Object[] { })", 24L);
133 | hotspot64BitWithCompressedOopsAndCMS.put("sizeOf(new Object[] { new Object(), new Object(), new Object(), new Object() })", 32L);
134 | hotspot64BitWithCompressedOopsAndCMS.put("sizeOf(new int[] { })", 24L);
135 | hotspot64BitWithCompressedOopsAndCMS.put("sizeOf(new int[] { 987654, 876543, 765432, 654321 })", 32L);
136 | hotspot64BitWithCompressedOopsAndCMS.put("deepSizeOf(new Pair(null, null))", 24L);
137 | hotspot64BitWithCompressedOopsAndCMS.put("deepSizeOf(new Pair(new Object(), null))", 40L);
138 | hotspot64BitWithCompressedOopsAndCMS.put("deepSizeOf(new Pair(new Object(), new Object()))", 56L);
139 | hotspot64BitWithCompressedOopsAndCMS.put("deepSizeOf(new ReentrantReadWriteLock())", 120L);
140 |
141 | CORRECT_SIZES.put(JvmInformation.HOTSPOT_64_BIT_WITH_COMPRESSED_OOPS_AND_CONCURRENT_MARK_AND_SWEEP, hotspot64BitWithCompressedOopsAndCMS);
142 | CORRECT_SIZES.put(JvmInformation.OPENJDK_64_BIT_WITH_COMPRESSED_OOPS_AND_CONCURRENT_MARK_AND_SWEEP, hotspot64BitWithCompressedOopsAndCMS);
143 |
144 | Map ibm32Bit = new HashMap<>();
145 | ibm32Bit.put("sizeOf(new Object())", 16L);
146 | ibm32Bit.put("sizeOf(new Integer(1))", 16L);
147 | ibm32Bit.put("sizeOf(1000)", 16L);
148 | ibm32Bit.put("deepSizeOf(new SomeClass(false))", 16L);
149 | ibm32Bit.put("deepSizeOf(new SomeClass(true))", 32L);
150 | ibm32Bit.put("sizeOf(new Object[] { })", 16L);
151 | ibm32Bit.put("sizeOf(new Object[] { new Object(), new Object(), new Object(), new Object() })", 32L);
152 | ibm32Bit.put("sizeOf(new int[] { })", 16L);
153 | ibm32Bit.put("sizeOf(new int[] { 987654, 876543, 765432, 654321 })", 32L);
154 | ibm32Bit.put("deepSizeOf(new Pair(null, null))", 24L);
155 | ibm32Bit.put("deepSizeOf(new Pair(new Object(), null))", 40L);
156 | ibm32Bit.put("deepSizeOf(new Pair(new Object(), new Object()))", 56L);
157 |
158 | CORRECT_SIZES.put(JvmInformation.IBM_32_BIT, ibm32Bit);
159 |
160 | Map ibm64Bit = new HashMap<>();
161 | ibm64Bit.put("sizeOf(new Object())", 24L);
162 | ibm64Bit.put("sizeOf(new Integer(1))", 32L);
163 | ibm64Bit.put("sizeOf(1000)", 32L);
164 | ibm64Bit.put("deepSizeOf(new SomeClass(false))", 32L);
165 | ibm64Bit.put("deepSizeOf(new SomeClass(true))", 56L);
166 | ibm64Bit.put("sizeOf(new Object[] { })", 24L);
167 | ibm64Bit.put("sizeOf(new Object[] { new Object(), new Object(), new Object(), new Object() })", 56L);
168 | ibm64Bit.put("sizeOf(new int[] { })", 24L);
169 | ibm64Bit.put("sizeOf(new int[] { 987654, 876543, 765432, 654321 })", 40L);
170 | ibm64Bit.put("deepSizeOf(new Pair(null, null))", 40L);
171 | ibm64Bit.put("deepSizeOf(new Pair(new Object(), null))", 64L);
172 | ibm64Bit.put("deepSizeOf(new Pair(new Object(), new Object()))", 88L);
173 |
174 | CORRECT_SIZES.put(JvmInformation.IBM_64_BIT, ibm64Bit);
175 |
176 | CORRECT_SIZES.put(JvmInformation.IBM_64_BIT_WITH_COMPRESSED_REFS, Collections.emptyMap());
177 |
178 | CORRECT_SIZES.put(JvmInformation.UNKNOWN_32_BIT, Collections.emptyMap());
179 | CORRECT_SIZES.put(JvmInformation.UNKNOWN_64_BIT, Collections.emptyMap());
180 |
181 | for (JvmInformation jvm : JvmInformation.values()) {
182 | Assert.assertThat(CORRECT_SIZES, hasKey(jvm));
183 | }
184 | }
185 |
186 | public static Long get(String expression) {
187 | return CORRECT_SIZES.get(JvmInformation.CURRENT_JVM_INFORMATION).get(expression);
188 | }
189 |
190 | private SizeOfTestValues() {
191 | //not instantiable
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/src/test/java/org/ehcache/sizeof/annotations/AnnotationProxyFactoryTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof.annotations;
18 |
19 | import org.junit.Test;
20 |
21 | import static org.junit.Assert.assertEquals;
22 |
23 | /**
24 | * @author Alex Snaps
25 | */
26 | public class AnnotationProxyFactoryTest {
27 |
28 | @Test(expected = UnsupportedOperationException.class)
29 | public void NoDefaultValueInReferenceAnnotationAndNoImplementationInCustomThrowsException_Test() {
30 | CustomAnnotation customAnnotation = UsingCustomAnnotation.class.getAnnotation(CustomAnnotation.class);
31 | ReferenceAnnotation annotationProxy = AnnotationProxyFactory
32 | .getAnnotationProxy(customAnnotation, ReferenceAnnotation.class);
33 | annotationProxy.things();
34 | }
35 |
36 | @Test
37 | public void NoImplementationInCustomFallsBackToReferenceImplementation_Test() {
38 | CustomAnnotation customAnnotation = UsingCustomAnnotation.class.getAnnotation(CustomAnnotation.class);
39 | ReferenceAnnotation annotationProxy = AnnotationProxyFactory
40 | .getAnnotationProxy(customAnnotation, ReferenceAnnotation.class);
41 | assertEquals("hello", annotationProxy.version());
42 | }
43 |
44 |
45 | @Test
46 | public void IfMethodExistsInCustomAnnotationRedirectsToIt_Test() {
47 | CustomAnnotation customAnnotation = UsingCustomAnnotation.class.getAnnotation(CustomAnnotation.class);
48 | ReferenceAnnotation annotationProxy = AnnotationProxyFactory
49 | .getAnnotationProxy(customAnnotation, ReferenceAnnotation.class);
50 | assertEquals(true, annotationProxy.deprecated());
51 | }
52 |
53 | @Test
54 | public void IfMethodExistsInCustomAnnotationButReturnTypeIsDifferentFallsBackToReferenceImplementation_Test() {
55 | CustomAnnotation customAnnotation = UsingCustomAnnotation.class.getAnnotation(CustomAnnotation.class);
56 | ReferenceAnnotation annotationProxy = AnnotationProxyFactory
57 | .getAnnotationProxy(customAnnotation, ReferenceAnnotation.class);
58 | assertEquals(5, annotationProxy.differentReturnType());
59 | }
60 |
61 | @Test
62 | public void DifferentReturnTypesFromDifferentMethods_Test() {
63 | CustomAnnotation customAnnotation = UsingCustomAnnotation.class.getAnnotation(CustomAnnotation.class);
64 | ReferenceAnnotation annotationProxy = org
65 | .ehcache
66 | .sizeof
67 | .annotations
68 | .AnnotationProxyFactory
69 | .getAnnotationProxy(customAnnotation, ReferenceAnnotation.class);
70 | assertEquals(Integer.class, annotationProxy.aClass());
71 | assertEquals(ExampleEnum.TWO, annotationProxy.anEnum());
72 | assertEquals(customAnnotation.anAnnotation(), annotationProxy.anAnnotation());
73 | }
74 |
75 |
76 | @CustomAnnotation
77 | class UsingCustomAnnotation {
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/test/java/org/ehcache/sizeof/annotations/CustomAnnotation.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof.annotations;
18 |
19 | import java.lang.annotation.ElementType;
20 | import java.lang.annotation.Retention;
21 | import java.lang.annotation.RetentionPolicy;
22 | import java.lang.annotation.Target;
23 |
24 | @Retention(RetentionPolicy.RUNTIME)
25 | @Target({ ElementType.FIELD, ElementType.TYPE, ElementType.PACKAGE })
26 | public @interface CustomAnnotation {
27 | boolean deprecated() default true;
28 |
29 | short differentReturnType() default 10;
30 |
31 | Class aClass() default Integer.class;
32 |
33 | ExampleEnum anEnum() default ExampleEnum.TWO;
34 |
35 | Deprecated anAnnotation() default @Deprecated;
36 | }
37 |
--------------------------------------------------------------------------------
/src/test/java/org/ehcache/sizeof/annotations/ExampleEnum.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof.annotations;
18 |
19 | /**
20 | * @author Alex Snaps
21 | */
22 | public enum ExampleEnum {
23 | ONE, TWO
24 | }
25 |
--------------------------------------------------------------------------------
/src/test/java/org/ehcache/sizeof/annotations/ReferenceAnnotation.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof.annotations;
18 |
19 | import java.lang.annotation.ElementType;
20 | import java.lang.annotation.Retention;
21 | import java.lang.annotation.RetentionPolicy;
22 | import java.lang.annotation.Target;
23 |
24 | @Retention(RetentionPolicy.RUNTIME)
25 | @Target({ ElementType.FIELD, ElementType.TYPE, ElementType.PACKAGE })
26 | public @interface ReferenceAnnotation {
27 | String version() default "hello";
28 |
29 | boolean deprecated() default false;
30 |
31 | int differentReturnType() default 5;
32 |
33 | String[] things();
34 |
35 | Class aClass() default String.class;
36 |
37 | ExampleEnum anEnum() default ExampleEnum.ONE;
38 |
39 | Deprecated anAnnotation() default @Deprecated;
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/src/test/java/org/ehcache/sizeof/filteredtest/AnnotationFilteredPackage.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof.filteredtest;
18 |
19 | /**
20 | * @author Alex Snaps
21 | */
22 | public class AnnotationFilteredPackage {
23 |
24 | private final byte[] bigArray = new byte[16 * 1024];
25 | }
26 |
--------------------------------------------------------------------------------
/src/test/java/org/ehcache/sizeof/filteredtest/custom/CustomAnnotationFilteredPackage.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof.filteredtest.custom;
18 |
19 | /**
20 | * @author Alex Snaps
21 | */
22 | public class CustomAnnotationFilteredPackage {
23 | }
24 |
--------------------------------------------------------------------------------
/src/test/java/org/ehcache/sizeof/filteredtest/custom/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | @IgnoreSizeOf package org.ehcache.sizeof.filteredtest.custom;
18 |
19 | import com.terracotta.ehcache.special.annotation.IgnoreSizeOf;
20 |
--------------------------------------------------------------------------------
/src/test/java/org/ehcache/sizeof/filteredtest/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | @IgnoreSizeOf package org.ehcache.sizeof.filteredtest;
18 |
19 | import org.ehcache.sizeof.annotations.IgnoreSizeOf;
--------------------------------------------------------------------------------
/src/test/java/org/ehcache/sizeof/filters/TypeFilterTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof.filters;
18 |
19 | import org.junit.Test;
20 |
21 | import java.lang.reflect.Field;
22 | import java.util.Collection;
23 | import java.util.Collections;
24 | import java.util.HashSet;
25 |
26 | import static org.hamcrest.CoreMatchers.is;
27 | import static org.junit.Assert.assertThat;
28 |
29 | /**
30 | * @author Alex Snaps
31 | */
32 | public class TypeFilterTest {
33 |
34 | @Test
35 | public void testStoresClassesToFilter() {
36 | TypeFilter filter = new TypeFilter();
37 | final Class klazz = String.class;
38 | assertThat(filter.filterClass(klazz), is(true));
39 | filter.addClass(klazz, true);
40 | assertThat(filter.filterClass(klazz), is(false));
41 | }
42 |
43 | @Test
44 | public void testStoresFieldsToFilter() throws NoSuchFieldException {
45 | TypeFilter filter = new TypeFilter();
46 | final Field field = String.class.getDeclaredField("value");
47 | final HashSet fields = new HashSet<>();
48 | Collections.addAll(fields, String.class.getDeclaredFields());
49 | assertThat(fields.contains(field), is(true));
50 | filter.addField(field);
51 | final Collection filtered = filter.filterFields(String.class, fields);
52 | assertThat(filtered.contains(field), is(false));
53 | assertThat(filtered.isEmpty(), is(false));
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/test/java/org/ehcache/sizeof/impl/AgentLoaderRaceTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof.impl;
18 |
19 | import org.junit.Assert;
20 | import org.junit.Assume;
21 | import org.junit.BeforeClass;
22 | import org.junit.Test;
23 |
24 | import java.net.URL;
25 | import java.net.URLClassLoader;
26 | import java.util.Arrays;
27 | import java.util.List;
28 | import java.util.concurrent.Callable;
29 | import java.util.concurrent.ExecutionException;
30 | import java.util.concurrent.ExecutorService;
31 | import java.util.concurrent.Executors;
32 | import java.util.concurrent.Future;
33 |
34 | import static org.hamcrest.core.AnyOf.anyOf;
35 | import static org.hamcrest.core.IsNot.not;
36 | import static org.hamcrest.core.IsNull.nullValue;
37 | import static org.hamcrest.core.StringStartsWith.startsWith;
38 |
39 | /**
40 | * @author Alex Snaps
41 | */
42 | public class AgentLoaderRaceTest {
43 |
44 | /*
45 | * This test tries to expose an agent loading race seen in MNK-3255.
46 | *
47 | * To trigger a failure the locking in AgentLoader.loadAgent has to be removed
48 | * and a sleep can be added after the check but before the load to open the
49 | * race wider.
50 | */
51 | @Test
52 | public void testAgentLoaderRace() throws InterruptedException, ExecutionException {
53 | final URL[] urls = ((URLClassLoader)AgentSizeOf.class.getClassLoader()).getURLs();
54 |
55 | Callable agentLoader1 = new Loader(new URLClassLoader(urls, null));
56 | Callable agentLoader2 = new Loader(new URLClassLoader(urls, null));
57 |
58 | ExecutorService executor = Executors.newFixedThreadPool(2);
59 | List> results = executor.invokeAll(Arrays.asList(agentLoader1, agentLoader2));
60 |
61 |
62 | for (Future f : results) {
63 | Assert.assertThat(f.get(), nullValue());
64 | }
65 | }
66 |
67 | static class Loader implements Callable {
68 |
69 | private final ClassLoader loader;
70 |
71 | Loader(ClassLoader loader) {
72 | this.loader = loader;
73 | }
74 |
75 | public Throwable call() {
76 | try {
77 | loader.loadClass(AgentSizeOf.class.getName()).newInstance();
78 | return null;
79 | } catch (Throwable t) {
80 | return t;
81 | }
82 | }
83 |
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/test/java/org/ehcache/sizeof/impl/AgentLoaderSystemPropTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof.impl;
18 |
19 | import org.junit.After;
20 | import org.junit.Test;
21 |
22 | import static org.hamcrest.CoreMatchers.notNullValue;
23 | import static org.hamcrest.Matchers.is;
24 | import static org.junit.Assert.assertThat;
25 |
26 | /**
27 | * @author Alex Snaps
28 | */
29 | public class AgentLoaderSystemPropTest {
30 |
31 | @After
32 | public void after() {
33 | System.getProperties().remove("org.ehcache.sizeof.agent.instrumentationSystemProperty");
34 | System.getProperties().remove(AgentLoader.INSTRUMENTATION_INSTANCE_SYSTEM_PROPERTY_NAME);
35 | }
36 |
37 | @Test
38 | public void testLoadsAgentIntoSystemPropsWhenRequired() {
39 | System.setProperty("org.ehcache.sizeof.agent.instrumentationSystemProperty", "true");
40 | AgentLoader.loadAgent();
41 | assertThat(AgentLoader.agentIsAvailable(), is(true));
42 | assertThat(System.getProperties().get(AgentLoader.INSTRUMENTATION_INSTANCE_SYSTEM_PROPERTY_NAME), notNullValue());
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/test/java/org/ehcache/sizeof/impl/AgentLoaderTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof.impl;
18 |
19 | import org.junit.After;
20 | import org.junit.Test;
21 |
22 | import static org.hamcrest.CoreMatchers.is;
23 | import static org.hamcrest.CoreMatchers.nullValue;
24 | import static org.junit.Assert.assertThat;
25 |
26 | /**
27 | * @author Alex Snaps
28 | */
29 | public class AgentLoaderTest {
30 |
31 | @After
32 | public void after() {
33 | System.getProperties().remove("org.ehcache.sizeof.agent.instrumentationSystemProperty");
34 | System.getProperties().remove(AgentLoader.INSTRUMENTATION_INSTANCE_SYSTEM_PROPERTY_NAME);
35 | }
36 |
37 | @Test
38 | public void testLoadsAgentProperly() {
39 | assertThat(Boolean.getBoolean("org.ehcache.sizeof.agent.instrumentationSystemProperty"), is(false));
40 | AgentLoader.loadAgent();
41 | assertThat(AgentLoader.agentIsAvailable(), is(true));
42 | assertThat(System.getProperties().get(AgentLoader.INSTRUMENTATION_INSTANCE_SYSTEM_PROPERTY_NAME), nullValue());
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/test/java/org/ehcache/sizeof/impl/JvmInformationTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright Terracotta, Inc.
3 | * Copyright IBM Corp. 2024, 2025
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.ehcache.sizeof.impl;
18 |
19 | import org.junit.Test;
20 |
21 | import static org.hamcrest.CoreMatchers.is;
22 | import static org.junit.Assert.assertThat;
23 |
24 | /**
25 | * Basic test used during refactoring of enum to make sure no differences were introduced
26 | */
27 | public class JvmInformationTest {
28 |
29 | @Test
30 | public void hotspot32Bits() {
31 | verifyJvmInfo(JvmInformation.HOTSPOT_32_BIT, 0, 0, 4, 8, 8, 8, 4, true, true, true);
32 | verifyJvmInfo(JvmInformation.HOTSPOT_32_BIT_WITH_CONCURRENT_MARK_AND_SWEEP, 0, 0, 4, 16, 8, 8, 4, true, true, true);
33 | }
34 |
35 | @Test
36 | public void hotspot64Bits() {
37 | verifyJvmInfo(JvmInformation.HOTSPOT_64_BIT, 0, 0, 8, 8, 8, 16, 8, true, true, true);
38 | verifyJvmInfo(JvmInformation.HOTSPOT_64_BIT_WITH_CONCURRENT_MARK_AND_SWEEP, 0, 0, 8, 24, 8, 16, 8, true, true, true);
39 | verifyJvmInfo(JvmInformation.HOTSPOT_64_BIT_WITH_COMPRESSED_OOPS, 0, 0, 4, 8, 8, 12, 8, true, true, true);
40 | verifyJvmInfo(JvmInformation.HOTSPOT_64_BIT_WITH_COMPRESSED_OOPS_AND_CONCURRENT_MARK_AND_SWEEP, 0, 0, 4, 24, 8, 12, 8, true, true, true);
41 | }
42 |
43 | @Test
44 | public void openJdk32Bits() {
45 | verifyJvmInfo(JvmInformation.OPENJDK_32_BIT, 0, 0, 4, 8, 8, 8, 4, true, true, true);
46 | verifyJvmInfo(JvmInformation.OPENJDK_32_BIT_WITH_CONCURRENT_MARK_AND_SWEEP, 0, 0, 4, 16, 8, 8, 4, true, true, true);
47 | }
48 |
49 | @Test
50 | public void openJdk64Bits() {
51 | verifyJvmInfo(JvmInformation.OPENJDK_64_BIT, 0, 0, 8, 8, 8, 16, 8, true, true, true);
52 | verifyJvmInfo(JvmInformation.OPENJDK_64_BIT_WITH_CONCURRENT_MARK_AND_SWEEP, 0, 0, 8, 24, 8, 16, 8, true, true, true);
53 | verifyJvmInfo(JvmInformation.OPENJDK_64_BIT_WITH_COMPRESSED_OOPS, 0, 0, 4, 8, 8, 12, 8, true, true, true);
54 | verifyJvmInfo(JvmInformation.OPENJDK_64_BIT_WITH_COMPRESSED_OOPS_AND_CONCURRENT_MARK_AND_SWEEP, 0, 0, 4, 24, 8, 12, 8, true, true, true);
55 | }
56 |
57 | @Test
58 | public void ibm32Bits() {
59 | verifyJvmInfo(JvmInformation.IBM_32_BIT, 0, 0, 4, 8, 8, 16, 4, true, false, true);
60 | }
61 |
62 | @Test
63 | public void ibm64Bits() {
64 | verifyJvmInfo(JvmInformation.IBM_64_BIT, 0, 0, 8, 8, 8, 24, 8, true, false, true);
65 | verifyJvmInfo(JvmInformation.IBM_64_BIT_WITH_COMPRESSED_REFS, 0, 0, 4, 8, 8, 16, 4, true, false, true);
66 | }
67 |
68 | @Test
69 | public void unknown() {
70 | verifyJvmInfo(JvmInformation.UNKNOWN_32_BIT, 0, 0, 4, 8, 8, 8, 4, true, true, true);
71 | verifyJvmInfo(JvmInformation.UNKNOWN_64_BIT, 0, 0, 8, 8, 8, 16, 8, true, true, true);
72 | }
73 |
74 | private void verifyJvmInfo(JvmInformation jvmInfo, int agentSizeOfAdj, int fieldOffsetAdj, int javaPointerSize, int minObjSize, int objAlign, int objHeaderSize, int pointerSize,
75 | boolean supportAgentSizeOf, boolean supportReflectionSizeOf, boolean supportUnsafeSizeOf) {
76 | assertThat(jvmInfo.getAgentSizeOfAdjustment(), is(agentSizeOfAdj));
77 | assertThat(jvmInfo.getFieldOffsetAdjustment(), is(fieldOffsetAdj));
78 | assertThat(jvmInfo.getJavaPointerSize(), is(javaPointerSize));
79 | assertThat(jvmInfo.getMinimumObjectSize(), is(minObjSize));
80 | assertThat(jvmInfo.getObjectAlignment(), is(objAlign));
81 | assertThat(jvmInfo.getObjectHeaderSize(), is(objHeaderSize));
82 | assertThat(jvmInfo.getPointerSize(), is(pointerSize));
83 | assertThat(jvmInfo.supportsAgentSizeOf(), is(supportAgentSizeOf));
84 | assertThat(jvmInfo.supportsReflectionSizeOf(), is(supportReflectionSizeOf));
85 | assertThat(jvmInfo.supportsUnsafeSizeOf(), is(supportUnsafeSizeOf));
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/test/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
17 |
18 |
19 |
20 |
21 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/test/resources/services/org.ehcache.sizeof.FilterConfigurator.txt:
--------------------------------------------------------------------------------
1 | org.ehcache.sizeof.SizeOfFilterSourceTest$TestMutator
--------------------------------------------------------------------------------